Modules and Reflective Access in Java

In the preceding sections, you saw that the module system enforces encapsulation. A module can only access explicitly exported packages from another module. In the past, it was always possible to overcome pesky access
restrictions by using reflection. As you have seen in Chapter 5 of Volume I, reflection can access private members of any class.

However, in the modular world, that is no longer true. If a class is inside a module, reflective access to non-public members will fail. Specifically, recall how we accessed private fields:

Field f = obj.getClass().getDeclaredField(“salary”);

f.setAccessibte(true);

double value = f.getDouble(obj);

f.setDouble(obj, value * 1.1);

The call f.setAccessible(true) succeeds unless a security manager disallows private field access. However, it is not common to run Java applications with security managers, and there are many libraries that use reflective access. Typical ex­amples are object-relational mappers, such as JPA, that automatically persist objects in databases and libraries that convert between objects and XML or JSON, such as JAXB and JSON-B.

If you use such a library, and you also want to use modules, you have to be careful. To demonstrate this issue, let us place the ObjectAnalyzer class from Chapter 5 of Volume I into a module com.horstmann.util. That class has a toString method that prints the fields of an object, using reflection.

A separate v2ch09.openpkg module contains a simple Country class:

package com.horstmann.places;

public class Country

{

private String name;

private double area;

public Country(String name, double area)

{

this.name = name;

this.area = area;

}

// . . .

}

A short program demonstrates how to analyze a Country object:

package com.horstmann.places;

import com.horstmann.util.*;

public class Demo

{

public static void main(String[] args) throws ReflectiveOperationException

{

var belgium = new Country(“Belgium”, 30510);

var analyzer = new ObjectAnalyzer();

System.out.println(analyzer.toString(belgium));

}

}

Now compile both modules and the Demo program:

javac com.horstmann.util/module-info.java \

com.horstmann.util/com/horstmann/util/ObjectAnalyzer.java

javac -p com.horstmann.util v2ch09.openpkg/module-info.java \

v2ch09.openpkg/com/horstmann/places/*.java

java -p v2ch09.openpkg:com.horstmann.util -m v2ch09.openpkg/com.horstmann.places.Demo

The program will fail with an exception:

Exception in thread “main” java.lang.reflect.InaccessibleObjectException:

Unable to make field private java.lang.String com.horstmann.places.Country.name

accessible: module v2ch09.openpkg does not “opens com.horstmann.places” to module

com.horstmann.util

Of course, in pristine theory, it is wrong to violate encapsulation and poke around in the private members of an object. But mechanisms such as object- relational mapping or XML/JSON binding are so common that the module system must accommodate them.

Using the opens keyword, a module can open a package, which enables reflective access to all instances of classes in the given package. Here is what our module has to do:

module v2ch09.openpkg

{

requires com.horstmann.util;

opens com.horstmann.places;

}

With this change, the ObjectAnalyzer will work correctly.

A module can be declared as open, such as

open module v2ch09.openpkg

{

requires com.horstmann.util;

}

An open module grants runtime access to all of its packages, as if all packages had been declared with exports and opens. However, only explicitly exported packages are accessible at compile time. Open modules combine the compile­time safety of the module system with the classic permissive runtime behavior.

Recall from Chapter 5 of Volume I that JAR files can contain, in addition to class files and a manifest, file resources which can be loaded with the method Class.getResourceAsStream, and now also with Module.getResourceAsStream. If a resource is stored in a directory that matches a package in a module, then the package must be opened to the caller. Resources in other directories, as well as the class files and manifest, can be read by anyone.

Source: Horstmann Cay S. (2019), Core Java. Volume II – Advanced Features, Pearson; 11th edition.

Leave a Reply

Your email address will not be published. Required fields are marked *