I'm playing around with modules and am trying to use reflection to invoke a method on a proxy. I get the following error:
java.lang.IllegalAccessException: class ca.example.MyStore.ProxyExample (in module ca.exampe.MyStore) cannot access class com.sun.proxy.jdk.proxy2.$Proxy10 (in module jdk.proxy2) because module jdk.proxy2 does not export com.sun.proxy.jdk.proxy2 to module ca.example.MyStore
Here is the method invocation on the proxy instance that causes the above exception when the default method is attempted to be invoked:
public Object executeDefaultMethod() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
// can not use Proxy when the app is modularized unless you specify that the module is open
// open module notes: https://stackoverflow.com/questions/53927375/how-do-java-module-directives-impact-reflection-access-into-a-module
Object proxy = Proxy.newProxyInstance(getSystemClassLoader(), new Class<?>[] { Coupon.class },
(prox, method, args) -> {
if (method.isDefault()) {
// invokeDefault is new with Java 16
return InvocationHandler.invokeDefault(prox, method, args);
}
return null;
}
);
Method method = proxy.getClass().getMethod("itemDiscount");
return method.invoke(proxy);
}
Interestingly, if I open
the module, the code works. Why is this? The reflective call is happening within code that exists in the module. What is this error telling me?
The reason the example in the question fails is due to changes in JDK 16 that make strong encapsulation of JDK internals the default setting as described in https://openjdk.org/jeps/396.
JDK 9 introduced the module system and to aid in migration, weak encapsulation was the default setting.
Starting with JDK 16, strong encapsulation is the default setting. See the relevant detail in the JEP here as it relates to the example in the question:
In Java 9, we improved both the security and the maintainability of the JDK by leveraging modules to limit access to its internal elements. Modules provide strong encapsulation, which means that
Code outside of a module can only access the public and protected elements of the packages exported by that module
This is why exporting the package the Coupon
class in the module-info.java
file allows the above example to work:
exports ca.MyStore.domain.coupons
Alternatively, one can define the module itself as an open
module. This works, but is probably not recommended.