在 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 这是正确的但不适用于此处。辅助功能基于两件事:

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

你没有写关于 2 的任何内容。但是如果 module library { exports org.reflection.lib; },那么你可以自由使用反射。原因是当您开始使用反射 API.

时,读取边缘会自动添加

无法访问的类型

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

  • 创建类型public并导出包(可能只到访问模块)
  • 打开包(可能只对访问模块)或模块
  • 添加命令行选项以实现上述任一功能

要了解它们的比较情况,请查看这篇关于 reflection vs encapsulation in Java 9 的文章。