本文主要研究下java9 opens與exports的區別html
主要用於解決deep reflection問題,open的做用是表示該模塊下的全部的包在runtime都容許deep reflection(包括public及private類型
)
可是編譯時期,僅僅容許該module中聲明過exports的包能夠訪問,若是沒有exports則該包的類在編譯時期不可讀
用於聲明該模塊的指定包在runtime容許使用反射訪問
表示容許在編譯時和運行時訪問指定包的public成員
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..."); } }
Sub1Service sub1Service = new Sub1Service(); Method privateMethod = sub1Service.getClass().getDeclaredMethod("privateMethod"); privateMethod.setAccessible(true); privateMethod.invoke(sub1Service);
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);
module packt.lib { exports com.packt.lib; }
這裏沒有exports及opens com.packt.lib.sub1
編譯報錯
)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)
newInstance運行時報錯
)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)
module packt.lib { exports com.packt.lib; opens com.packt.lib.sub1; }
因爲沒有exports,則編譯不過
[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 中聲明, 但該模塊未導出它)
像上面那種直接引用包名來反射的,不會報錯,由於編譯能夠經過,運行正常
module packt.lib { exports com.packt.lib; exports com.packt.lib.sub1; }
能夠編譯經過,運行報錯
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.
能夠編譯經過,運行報錯
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 module packt.lib { exports com.packt.lib; }
這種狀況,若是是直接訪問該類來使用反射,因爲沒有exports該package,則直接編譯報錯
這種狀況編譯能夠經過,運行正常
open module packt.lib { exports com.packt.lib; exports com.packt.lib.sub1; }
兩種訪問方式的反射均正常編譯及運行。
open的做用是表示該模塊下的全部的包在runtime都容許deep reflection(包括public及private類型
);opens package的做用只是容許該包在runtime都容許deep reflectionopen及opens都僅僅是開放runtime時期的能夠經過反射訪問(
蘊含了運行時的exports
)。java
編譯及運行時
)若是反射不直接經過類名調用,只是運行時經過包名使用,則只需open或opens便可
若是是經過類名來反射,因爲用到了該類,須要經過exports指定能夠訪問,不指定則編譯期當即報錯
若是是經過類名來反射使用public方法或newInstance,若是沒有exports,則運行時報錯
若是有exports,可是沒有open,所以編譯經過運行時報錯
--illegal-access默認是permit,表示容許unnamed modules反射(java.lang.reflect/java.lang.invoke)使用全部named modules中的類