Search code examples
javamodulejava-module

module jdk.proxy2 does not export com.sun.proxy.jdk.proxy2 to module ca.example.MyStore


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?


Solution

  • 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.