Close

Java 9 Modules - Reflective Access via open and opens clauses

[Last Updated: Mar 14, 2018]

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.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
            • PropertyUtil.java
        • the.client.app
          • com
            • logicbig

    See Also