Reputation: 651
Suppose I have an application that given a well known package name and class, I need to load that class reflectively and invoke some methods from it. Let's say this class is available in another java jar (the "library").
So far, before java 9, since all the classes in the classpath were available, it was straightforward to achieve this.
However, with Java 9, if the application "module" does not require the "library" package, the library class is not accessible.
How can I achieve the same behavior using modularized jars?
Example:
This is my application:
// This is defined in ReflectionApp.jar
// ReflectionApp
// └── src
// ├── module-info.java
// └── org
// └── reflection
// └── app
// └── ReflectionApp.java
//
package org.reflection.app;
import java.lang.reflect.Method;
public class ReflectionApp {
private static final String PACKAGE = "org.reflection.lib";
private static final String CLASS = "ReflectiveClass";
private static final String METHOD = "doSomeWork";
public static void main(String[] args) throws Exception {
// This will not work using java9
Class<?> klass = Class.forName(String.format("%s.%s", PACKAGE, CLASS));
Object obj = klass.getDeclaredConstructor().newInstance();
Method meth = klass.getDeclaredMethod(METHOD);
meth.invoke(obj);
}
}
And this is my library
// This is defined in ReflectionLib.jar
// ReflectionLib
// └── src
// ├── module-info.java
// └── org
// └── reflection
// └── lib
// └── ReflectiveClass.java
//
package org.reflection.lib;
public class ReflectiveClass {
public void doSomeWork() {
System.out.println("::: Doing some work!");
}
}
At the moment if I try to call it like this (assuming all jars are created and available in lib directory):
java --module-path lib -m ReflectionApp/org.reflection.app.ReflectionApp
Then I get a Exception in thread "main" java.lang.ClassNotFoundException: org.reflection.lib.ReflectiveClass
--- SOLUTION ---
Thanks to @Nicolai I was able to make it run by adding --add-modules
parameter to the command.
⇒ java --module-path lib --add-modules ReflectionLib -m ReflectionApp/org.reflection.app.ReflectionApp
::: Doing some work!
Upvotes: 8
Views: 2105
Reputation: 39536
If the first module does not depend on the second module via the requires
clause in module-info.java
, then the second module is not in the transitive closure of the first module's dependencies. Therefore the second module is not observable to the runtime and your reflection fails. In this case, you can explicitly add your module to the module graph via --add-modules
option:
--add-modules <module-name>
In your case <module-name>
should be substituted with the name of the ReflectionLib
module.
Upvotes: 1
Reputation: 51030
There are plenty of ways to make reflection work but none of them are as straight-forward as the "it just works" way before the module system. Let me narrow them down for your particular problem.
But before that you have to make sure that the library module actually makes it into the module graph. If it is not transitively required by the initial module, you can use --add-modules library
to add it to the graph.
You write that "if the application 'module' does not require the 'library' package, the library class is not accessible". Assuming you meant module instead of package that is true but does not apply here. Accessibility builds on two things:
You didn't write anything regarding 2. but if module library { exports org.reflection.lib; }
, then you are free to use reflection. The reason is that the reads edge is added automatically when you start using the reflection API.
Now it gets more interesting. If the library module does not export org.reflection.lib
or if ReflectiveClass
were not public, the above approach does not work. In that case you have plenty of options to choose from:
To see how they compare have a look at this article about reflection vs encapsulation in Java 9.
Upvotes: 8