In this tutorial we will learn how reflective access are allowed when applied across Java 9 modules.
From AccessibleObject#setAccessible(boolean):
This method may be used by a caller in class C to enable access to a member of declaring class D if any of the following hold:
C and D are in the same module.
- The member is
public and D is public in a package that the module containing D exports to at least the module containing C .
- The member is
protected static , D is public in a package that the module containing D exports to at least the module containing C , and C is a subclass of D .
D is in a package that the module containing D opens to at least the module containing C . All packages in unnamed and open modules are open to all modules and so this method always succeeds when D is in an unnamed or open module.
This method cannot be used to enable access to private members, members with default (package) access, protected instance members, or protected constructors when the declaring class is in a different module to the caller and the package containing the declaring class is not open to the caller's module.
Note that AccessibleObject is extended by Field , Method and Constructor classes.
To make an inaccessible field/method/constructor accessible, the module declaration must use one of the following clauses:
- open
- opens
- opens .... to ....
Let's say a framework module 'framework.module' uses reflection to provide dynamic feature with the following module-info.java:
module framework.module{
exports some.package;
}
A client module wants to use the framework module so it requires it:
module client.module{
requires framework.module;
}
For reflective access, the client module should open itself with one of the following declarations:
(1) Open the entire client module for reflection:
open module client.module{
requires framework.module;
}
(2) Open only selected package(s) for reflection:
module client.module{
opens some.client.package;
requires framework.module;
}
(3) Open the selected package(s) to a specific framework module:
module client.module{
opens some.client.package to framework.module;
requires framework.module;
}
Let's see a complete example to understand how that works.
Example
A Framework Module
some.framework/com/util/PropertyUtil.javapackage com.util;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class PropertyUtil {
public static Map<String, Object> getProperties(Object object) throws IllegalAccessException {
Class<?> theClass = object.getClass();
Field[] declaredFields = theClass.getDeclaredFields();
Map<String, Object> fieldsMap = new HashMap<>();
for (Field declaredField : declaredFields) {
declaredField.setAccessible(true);
Object o = declaredField.get(object);
fieldsMap.put(declaredField.getName(), o);
}
return fieldsMap;
}
}
some.framework/module-info.javamodule some.framework {
exports com.util;
}
The Client Module
the.client.app/com/logicbig/AppMain.javapackage com.logicbig;
import com.util.PropertyUtil;
import java.util.Map;
public class AppMain {
public static void main(String[] args) throws IllegalAccessException {
MyObj obj = new MyObj(4, "test string");
Map<String, Object> map = PropertyUtil.getProperties(obj);
System.out.println(map);
}
}
the.client.app/com/logicbig/MyObj.javapackage com.logicbig;
public class MyObj {
private int anInt;
private String aString;
public MyObj(int anInt, String aString) {
this.anInt = anInt;
this.aString = aString;
}
}
the.client.app/module-info.javamodule the.client.app {
opens com.logicbig to some.framework;
requires some.framework;
}
Here is our project's directory structure (we are using multi-module layout).
D:\modules-reflective-access-example>"tree /A /F" \---src +---some.framework | | module-info.java | | | \---com | \---util | PropertyUtil.java | \---the.client.app | module-info.java | \---com \---logicbig AppMain.java MyObj.java
Compiling
D:\modules-reflective-access-example>dir /B /S src\*.java > javaFiles.txt
D:\modules-reflective-access-example>type javaFiles.txt D:\modules-reflective-access-example\src\some.framework\module-info.java D:\modules-reflective-access-example\src\some.framework\com\util\PropertyUtil.java D:\modules-reflective-access-example\src\the.client.app\module-info.java D:\modules-reflective-access-example\src\the.client.app\com\logicbig\AppMain.java D:\modules-reflective-access-example\src\the.client.app\com\logicbig\MyObj.java
D:\modules-reflective-access-example>javac -d outDir --module-source-path src @javaFiles.txt
After compilation:
D:\modules-reflective-access-example>"tree /A /F" | javaFiles.txt | +---outDir | +---some.framework | | | module-info.class | | | | | \---com | | \---util | | PropertyUtil.class | | | \---the.client.app | | module-info.class | | | \---com | \---logicbig | AppMain.class | MyObj.class | \---src +---some.framework | | module-info.java | | | \---com | \---util | PropertyUtil.java | \---the.client.app | module-info.java | \---com \---logicbig AppMain.java MyObj.java
Running
D:\modules-reflective-access-example>java --module-path outDir --module the.client.app/com.logicbig.AppMain {aString=test string, anInt=4}
Without opens clause
Let's see what will happen if our client module does not use the 'opens' clause:
module the.client.app {
//opens com.logicbig to some.framework;
requires some.framework;
}
D:\modules-reflective-access-example>java --module-path outDir --module the.client.app/com.logicbig.AppMain Exception in thread "main" java.lang.reflect.InaccessibleObjectException: Unable to make field private int com.logicbig.MyObj.anInt accessible: module the.client.app does not "opens com.logicbig" to module lib.util
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(Unknown Source)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(Unknown Source)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Unknown Source)
at java.base/java.lang.reflect.Field.setAccessible(Unknown Source)
at lib.util/com.util.PropertyUtil.getProperties(PropertyUtil.java:13)
at the.client.app/com.logicbig.AppMain.main(AppMain.java:9)
Example ProjectDependencies and Technologies Used: |