在 Java9 中,如果我不知道它是模块,我如何反射性地加载 class?
In Java9 how can I reflectively load a class if I don't know it's module?
假设我有一个应用程序,它给出了一个众所周知的包名和 class,我需要反射性地加载那个 class 并从中调用一些方法。假设这个 class 在另一个 java jar("library")中可用。
到目前为止,在java 9之前,由于class路径中的所有classes都可用,因此很容易实现。
但是,对于 Java 9,如果应用程序 "module" 不需要 "library" 包,则库 class 不可访问。
如何使用模块化 jar 实现相同的行为?
示例:
这是我的申请:
// 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);
}
}
这是我的图书馆
// 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!");
}
}
目前,如果我尝试这样调用它(假设所有 jar 都已创建并在 lib 目录中可用):
java --module-path lib -m ReflectionApp/org.reflection.app.ReflectionApp
然后我得到一个Exception in thread "main" java.lang.ClassNotFoundException: org.reflection.lib.ReflectiveClass
--- 解决方案 ---
感谢@Nicolai,我能够通过向命令添加 --add-modules
参数来实现 运行。
⇒ java --module-path lib --add-modules ReflectionLib -m ReflectionApp/org.reflection.app.ReflectionApp
::: Doing some work!
如果第一个模块不通过 module-info.java
中的 requires
子句依赖于第二个模块,则第二个模块不在第一个模块依赖项的传递闭包中。因此,第二个模块对于运行时是不可观察的,并且您的反射失败。在这种情况下,您可以通过 --add-modules
选项明确地将您的模块添加到模块图中:
--add-modules <module-name>
在您的情况下,<module-name>
应替换为 ReflectionLib
模块的名称。
有 plenty of ways to make reflection work,但其中 none 与模块系统之前的 "it just works" 方式一样直接。让我针对您的特定问题缩小范围。
但在此之前,您必须确保 library 模块确实进入了模块图中。如果初始模块不传递需要,可以使用--add-modules library
将其添加到图中。
包已导出
你写那个"if the application 'module' does not require the 'library' package, the library class is not accessible"。假设您的意思是 module 而不是 package 这是正确的但不适用于此处。辅助功能基于两件事:
- 两个模块之间的读取边缘
- 导出访问类型的访问模块
你没有写关于 2 的任何内容。但是如果 module library { exports org.reflection.lib; }
,那么你可以自由使用反射。原因是当您开始使用反射 API.
时,读取边缘会自动添加
无法访问的类型
现在变得更有趣了。如果 library 模块不导出 org.reflection.lib
或者如果 ReflectiveClass
不是 public,则上述方法不起作用。在这种情况下,您有很多选择:
- 创建类型public并导出包(可能只到访问模块)
- 打开包(可能只对访问模块)或模块
- 添加命令行选项以实现上述任一功能
要了解它们的比较情况,请查看这篇关于 reflection vs encapsulation in Java 9 的文章。
假设我有一个应用程序,它给出了一个众所周知的包名和 class,我需要反射性地加载那个 class 并从中调用一些方法。假设这个 class 在另一个 java jar("library")中可用。
到目前为止,在java 9之前,由于class路径中的所有classes都可用,因此很容易实现。
但是,对于 Java 9,如果应用程序 "module" 不需要 "library" 包,则库 class 不可访问。
如何使用模块化 jar 实现相同的行为?
示例:
这是我的申请:
// 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);
}
}
这是我的图书馆
// 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!");
}
}
目前,如果我尝试这样调用它(假设所有 jar 都已创建并在 lib 目录中可用):
java --module-path lib -m ReflectionApp/org.reflection.app.ReflectionApp
然后我得到一个Exception in thread "main" java.lang.ClassNotFoundException: org.reflection.lib.ReflectiveClass
--- 解决方案 ---
感谢@Nicolai,我能够通过向命令添加 --add-modules
参数来实现 运行。
⇒ java --module-path lib --add-modules ReflectionLib -m ReflectionApp/org.reflection.app.ReflectionApp
::: Doing some work!
如果第一个模块不通过 module-info.java
中的 requires
子句依赖于第二个模块,则第二个模块不在第一个模块依赖项的传递闭包中。因此,第二个模块对于运行时是不可观察的,并且您的反射失败。在这种情况下,您可以通过 --add-modules
选项明确地将您的模块添加到模块图中:
--add-modules <module-name>
在您的情况下,<module-name>
应替换为 ReflectionLib
模块的名称。
有 plenty of ways to make reflection work,但其中 none 与模块系统之前的 "it just works" 方式一样直接。让我针对您的特定问题缩小范围。
但在此之前,您必须确保 library 模块确实进入了模块图中。如果初始模块不传递需要,可以使用--add-modules library
将其添加到图中。
包已导出
你写那个"if the application 'module' does not require the 'library' package, the library class is not accessible"。假设您的意思是 module 而不是 package 这是正确的但不适用于此处。辅助功能基于两件事:
- 两个模块之间的读取边缘
- 导出访问类型的访问模块
你没有写关于 2 的任何内容。但是如果 module library { exports org.reflection.lib; }
,那么你可以自由使用反射。原因是当您开始使用反射 API.
无法访问的类型
现在变得更有趣了。如果 library 模块不导出 org.reflection.lib
或者如果 ReflectiveClass
不是 public,则上述方法不起作用。在这种情况下,您有很多选择:
- 创建类型public并导出包(可能只到访问模块)
- 打开包(可能只对访问模块)或模块
- 添加命令行选项以实现上述任一功能
要了解它们的比较情况,请查看这篇关于 reflection vs encapsulation in Java 9 的文章。