在 Java9 中,如果我不知道类是模块,我如何反射性地加载它?



>假设我有一个应用程序,它给出了一个众所周知的包名称和类,我需要反射性地加载该类并从中调用一些方法。假设这个类在另一个java jar("库")中可用。

到目前为止,在 java 9 之前,由于类路径中的所有类都可用,因此实现这一点很简单。

但是,在 Java 9 中,如果应用程序"模块"不需要"库"包,则无法访问库类。

如何使用模块化罐子实现相同的行为?

例:

这是我的应用程序:

// 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!

有很多方法可以使反射工作,但没有一种方法像模块系统之前的"它只是工作"那样直接。让我针对您的特定问题缩小它们的范围。

但在此之前,您必须确保模块实际上进入了模块图。如果初始模块不需要它,则可以使用--add-modules library将其添加到图形中。

包已导出

你写道"如果应用程序'模块'不需要'库'包,则库类不可访问"。假设你的意思是模块而不是,这是真的,但在这里不适用。辅助功能建立在两件事上:

  1. 两个模块之间的读取边沿
  2. 导出访问类型的访问模块

你没有写任何关于 2. 但如果module library { exports org.reflection.lib; },那么你可以自由使用反射。原因是读取边缘是在开始使用反射 API 时自动添加的。

类型不可访问

现在它变得更有趣了。如果模块未导出org.reflection.lib或者ReflectiveClass不是公共的,则上述方法不起作用。在这种情况下,您有很多选项可供选择:

  • 使类型公开并导出包(可能仅导出到访问模块)
  • 打开
  • 包(可能仅打开访问模块)或模块
  • 添加命令行选项以实现上述任何一项

要了解它们如何比较,请查看这篇关于Java 9中的反射与封装的文章。

如果第一个模块不依赖于第二个模块通过module-info.java中的requires子句,则第二个模块不在第一个模块依赖项的传递闭包中。因此,运行时无法观察到第二个模块,并且反射失败。在这种情况下,您可以通过--add-modules选项将模块显式添加到模块图中:

--add-modules <module-name>

在您的情况下<module-name>应替换为ReflectionLib模块的名称。

最新更新