Java 9 Modules - Reflective Access via open and opens clauses

[Updated: Nov 7, 2017, Created: Nov 6, 2017]

In Java 9 Module system, the reflective access across the modules will not work by default. That's because a module normally should not be able to access another module's packages/classes if it is not exported by the owner module or this module is not requiring it.

To make a given module accessible to another module via reflection, it 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;
}

With above settings the framework.module can still use methods like Class.forName() or Class.getDeclaredFields() on the client classes, but the methods like Constructor.newInstance() or Field.get(), will not work unless the client module opens itself for reflection 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;
}

The client module is in control of whether to make itself accessible for reflection or not. That should not be other way around, because that will break the entire module system by enabling any module to access other modules (even non-exported features) via reflection.

Let's see a complete example to understand how that works.

Example

A Framework Module

some.framework/com/util/PropertyUtil.java

package 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.java

module some.framework {
  exports com.util;
}

The Client Module

the.client.app/com/logicbig/AppMain.java

package 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.java

package 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.java

module 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 Project

Dependencies and Technologies Used :

  • JDK 9.0.1
Java 9 Modules - Using opens/open clause Example Select All Download
  • modules-reflective-access-example
    • src
      • some.framework
        • com
          • util
      • the.client.app
        • com
          • logicbig

See Also