The difference between java9 opens and exports

  java9

Order

This paper mainly studies the difference between java9 opens and exports

Open and exports

open

  • open module

It is mainly used to solve the deep reflection problem. the function of open is to indicate that all packages under this module allow deep reflection (Including public and private types)
However, at compile time, only packages declared exports in the module are allowed to be accessed, and if there is no exports, the package’s class is unreadable at compile time.

  • opens package

The specified package used to declare the module allows reflective access at runtime.

exports

Represents a public member that allows access to the specified package at compile time and run time

Influence of open and exports on reflection

Reflection method

  • Target class
package com.packt.lib.sub1;
public class Sub1Service {
    public Sub1Service() {
        System.out.println("Sub1Service being instanced");
    }

    public void publicMethod() {
        System.out.println("public method called!");
    }

    protected void protectedMethod(){
        System.out.println("protected method called...");
    }


    private void privateMethod(){
        System.out.println("private method called...");
    }
}
  • Access class name reflection
        Sub1Service sub1Service = new Sub1Service();
        Method privateMethod = sub1Service.getClass().getDeclaredMethod("privateMethod");
        privateMethod.setAccessible(true);
        privateMethod.invoke(sub1Service);
  • Reflected by Package Name
        Optional<Module> optional = ModuleLayer.boot().findModule("packt.lib");
        Class clz = Class.forName(optional.get(),"com.packt.lib.sub1.Sub1Service");
        Object sub1 = clz.newInstance();
        System.out.println(sub1.getClass().getMethods());
        Method publicMethod = sub1.getClass().getDeclaredMethod("publicMethod");
        publicMethod.invoke(sub1);

        Method protectedMethod = sub1.getClass().getDeclaredMethod("protectedMethod");
        protectedMethod.setAccessible(true);
        protectedMethod.invoke(sub1);

        Method privateMethod = sub1.getClass().getDeclaredMethod("privateMethod");
        privateMethod.setAccessible(true);
        privateMethod.invoke(sub1);

No exports, no opens

  • module-info.java
module packt.lib {
    exports com.packt.lib;
}

There are no exports and openscom.packt.lib.sub1.

  • Reflect via class name (Compile error report)
Exception in thread "main" java.lang.IllegalAccessException: class com.packt.App (in module packt.main) cannot access class com.packt.lib.sub1.Sub1Service (in module packt.lib) because module packt.lib does not export com.packt.lib.sub1 to module packt.main
    at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361)
    at java.base/jdk.internal.reflect.Reflection.ensureMemberAccess(Reflection.java:107)
    at java.base/java.lang.Class.newInstance(Class.java:553)
    at packt.main/com.packt.App.main(App.java:25)
  • Through packet name reflection (The newInstance runtime reported an error.)
Exception in thread "main" java.lang.IllegalAccessException: class com.packt.App (in module packt.main) cannot access class com.packt.lib.sub1.Sub1Service (in module packt.lib) because module packt.lib does not export com.packt.lib.sub1 to module packt.main
    at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:361)
    at java.base/jdk.internal.reflect.Reflection.ensureMemberAccess(Reflection.java:107)
    at java.base/java.lang.Class.newInstance(Class.java:553)
    at packt.main/com.packt.App.main(App.java:26)

No exports, opens

  • module-info.java
module packt.lib {
    exports com.packt.lib;
    opens com.packt.lib.sub1;
}
  • Reflected by Class Name

Since there is no exports, it will not compile

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.6.2:compile (default-compile) on project main: Compilation failure
[ERROR] /Users/demo/java9-multi-module-demo/main/src/main/java/com/packt/App.java:[30,41] 程序包 com.packt.lib.sub1 不可见
[ERROR] (程序包 com.packt.lib.sub1 已在模块 packt.lib 中声明, 但该模块未导出它)
  • Reflected by Package Name

As the above kind of direct reference package name to reflect, will not report an error, because the compilation can pass, normal operation

Exports, no opens

module packt.lib {
    exports com.packt.lib;
    exports com.packt.lib.sub1;
}
  • Reflected by Class Name

Can compile through, run to report an error

Sub1Service being instanced
Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make private void com.packt.lib.sub1.Sub1Service.privateMethod() accessible: module packt.lib does not "opens com.packt.lib.sub1" to module packt.main
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:337)
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:281)
    at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:198)
    at java.base/java.lang.reflect.Method.setAccessible(Method.java:192)
    at packt.main/com.packt.App.main(App.java:44)
[ERROR] Command execution failed.
  • Reflected by Package Name

Can compile through, run to report an error

Sub1Service being instanced
[Ljava.lang.reflect.Method;@4157f54e
public method called!
Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make protected void com.packt.lib.sub1.Sub1Service.protectedMethod() accessible: module packt.lib does not "opens com.packt.lib.sub1" to module packt.main
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:337)
    at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:281)
    at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:198)
    at java.base/java.lang.reflect.Method.setAccessible(Method.java:192)
    at packt.main/com.packt.App.main(App.java:34)
[ERROR] Command execution failed.

Open the entire module directly, but there is no exports.

  • module-info.java
open module packt.lib {
    exports com.packt.lib;
}
  • Direct Access Class Reflection

In this case, if the class is directly accessed to use reflection, since there is no exports for this package, the error will be directly compiled and reported.

  • Reflected by Package Name

In this case, the compilation can pass, and the operation is normal.

Directly open the whole module, also have exports.

open module packt.lib {
    exports com.packt.lib;
    exports com.packt.lib.sub1;
}

The reflections of the two access methods are compiled and run normally.

Summary

  • Open means allow runtime use by reflection

The function of open is to indicate that all packages under this module allow deep reflection (Including public and private types); OpenPackage only allows the package to allow deep reflection at runtime.

Both open and OpenSS are only open runtime events that can be accessed through reflection (Contains runtime exports)。

  • Exports means allowing access to public members of the specified package (Compile and run time)

If the reflection is not called directly by the class name, but only used by the package name at runtime, then only open or opens is required.
If it is reflected by the class name, since the class is used, you need to specify that it is accessible through exports, otherwise the compile time will report an error immediately.
If the public method or newInstance is used for reflection through the class name, if there is no exports, the runtime reports an error.
If there are exports but there is no open, the compilation will report an error at runtime.

  • illegal-access

–illegal-access defaults to permit, which means that unnamed modules reflection (java.lang.reflect/java.lang.invoke) is allowed to use classes in all named modules

doc