如何使反射在 JDK 16 及更高版本上工作?

JJB*_*JJB -2 java reflection java-16 java-17

我将以下遗留代码迁移到 Java 16,但是由于这个新版本引入了强封装,它不起作用:

try {
    Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
    method.setAccessible(true);
    method.invoke(new URLClassLoader(
        new URL[] {}),
        new File("C:/external-folder/my.jar").toURI().toURL()
    );
} catch (Exception exc) {
    exc.printStackTrace();
}
Run Code Online (Sandbox Code Playgroud)

有办法让它发挥作用吗?

Bri*_*etz 25

问题不在于反射“不起作用”;而在于反射“不起作用”。反射最终强制执行编译器和运行时一直强制执行的更多可访问性模型。 URLClassLoader::addUrl仅适用于子类;它不适合从实现外部访问,而这正是您正在做的事情。随着时间的推移,从 Java 9 开始一直到后续版本(包括 17),访问限制越来越多地通过反射来识别,并发出警告,从而使损坏的代码有机会迁移到可支持的代码。

\n

所讨论的代码实际上只是意外地工作过;它取决于能否闯入不受支持的接口。使用setAccessible应该是一个线索。当然,你可以通过打破窗户进入锁着的房子,但如果你必须打破窗户(而且它\xe2\x80\x99不是你的房子),你应该意识到问题出在哪里。

\n

把它看作是半满的玻璃杯;这个意外工作的代码工作了很长时间。但账单已经到期了;是时候修复你的代码了。

\n

  • @ChrisF 如果你只是想在几个月后把罐头踢掉,那就做你必须做的事,但你很快就会回到同一个地方。修复代码以不依赖内部实现细节是可持续的解决方案。 (12认同)
  • @ChrisF 这里的教训是:“我尝试过,它有效”是你首先遇到这个问题的方式。这是一个从这次经验中学习的好机会,而不是重复同样的错误。 (11认同)
  • @ChrisF你正在尝试**破坏Java SE。**如果某些东西不是公开的,那就意味着你不应该使用它。如果您尝试绕过该保护,但后来失败了,那是您的错,而不是 Java SE 的错。这与道德无关;这与道德有关。这是关于写一些今天碰巧有效的东西,而不是写一些永远有效的可靠的东西。其他类型的工程师不接受创造注定会损坏的机器或结构;软件工程师也不应该。 (10认同)
  • [这很糟糕](https://github.com/burningwave/jvm-driver/blob/main/java/src/main/java/org/burningwave/jvm/DriverFunctionSupplierUnsafe.java#L311)。今天可能工作,明天可能不工作。如果/当它失败时,可能会导致整个 JVM 崩溃。(最坏的情况:“MethodHandles.Lookup”获得一个额外的引用字段 - 然后此代码可能会覆盖该字段。下一个 GC 周期可能会使虚拟机崩溃。) (8认同)
  • **今天可以上班**。是的,测试有帮助,但根据规范编写更能保证未来的发展。 (7认同)
  • 所有这些道德并不能解决我的问题,不像我在[这篇文章]中找到的工作代码(https://dev.to/jjbrt/how-to-make-reflection-filled-work-on-jdk-16-and -稍后-ihp) (3认同)
  • 如果这是您的应用程序,您可以使用此命令行参数“--add-opens=java.base/java.net=ALL-UNNAMED”(如果您使用命名模块,请将“ALL-UNNAMED”替换为您的模块)。如果这是一个库,则指示您的用户将其添加到他们的 java 命令行参数中。 (3认同)
  • @JohannesKuhn,这是一个很棒的软件,展示了 Java 的“*我们强烈封装一切,除了最糟糕的`sun.misc.Unsafe`*”的政策将导致什么结果。有趣的是,该代码的维护者似乎还没有注意到“Unsafe.defineAnonymousClass”在 JDK 17 中不再存在…… (2认同)

Nic*_*lai 10

代码非常奇怪。乍一看,我认为它使用反射来访问URLClassLoader内部以添加myJar现有的类加载器,但实际上它使用它来将其添加到新的类加载器。没有理由这样做——你可以只使用构造函数URLClassLoader

它应该看起来像这样(未经测试,因为我远离 IDE):

URL jar = new File("C:/external-folder/my.jar").toURI().toURL();
URL[] urls = { jar };
new URLClassLoader(urls);
Run Code Online (Sandbox Code Playgroud)

  • @Chris F,您是否有代码示例来演示为什么上述答案不起作用,以及何时需要问题代码?addUrl 的源代码更新与将 url 提供给构造函数时相同的成员,因此这个答案看起来很有帮助。 (7认同)