【问题标题】:How can I make reflection work on JDK 16 and later?如何使反射在 JDK 16 及更高版本上工作?
【发布时间】:2021-09-14 12:40:19
【问题描述】:

我将以下遗留代码迁移到 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();
}

有没有办法让它工作?

【问题讨论】:

  • 不要破解系统类加载器,而是将您的外部 .jar 文件重构为 service provider
  • 您正在使用此内部 API 将 JAR 添加到类路径中。根据您这样做的原因,有更好的(即支持的)方法来实现您的目标。您介意解释一下为什么需要添加该 JAR 吗?
  • 这是一个老代码,在运行时编译一个类,以后还要加载它
  • 您应该只使用其他方式加载该类,例如单独的类加载器。但是如果你想在下一个 java 版本中再次破解和修复它,你可以使用很多方法:instrumentation、unsafe、通过 unsafe 访问的内部查找

标签: java reflection java-16 java-17


【解决方案1】:

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

有问题的代码只是真的意外地工作过;它取决于能够闯入不受支持的界面。使用setAccessible 应该是一个线索。当然,你可以通过打破窗户进入上锁的房子,但如果你必须打破窗户(而且这不是你的房子),你应该知道问题出在哪里。

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

【讨论】:

  • @ChrisF 如果您只想在几个月后摆脱困境,那就去做您必须做的事情,但您很快就会回到原来的位置。将代码修复为不依赖内部实现细节是可持续的解决方案。
  • This is bad。今天可以上班,明天可以不上班。如果/当它失败时,它可能会使整个 JVM 崩溃。 (最坏的情况:MethodHandles.Lookup 获得一个额外的引用字段 - 然后此代码可能会覆盖该字段。下一个 GC 周期可能会使 VM 崩溃。)
  • 今天可以工作。是的,测试会有所帮助,但根据规范编写更具前瞻性。
  • @ChrisF 这里的教训是:“我试过了,它奏效了”是你首先解决这个问题的方式。这是从这次经历中学习的好机会,而不是重复同样的错误。
  • @ChrisF 您正试图破坏 Java SE。 如果某些内容不公开,则意味着您不应该使用它。如果你试图绕过这种保护,但后来它失败了,那是你的错,而不是 Java SE 的错。这与道德无关;它是关于写一些今天碰巧有效的东西,而不是写一些可靠的东西,永远有效。其他类型的工程师不接受制造注定要破裂的机器或结构;软件工程师也不应该。
【解决方案2】:

代码很奇怪。乍一看,我以为它使用反射来访问URLClassLoader 内部以将myJar 添加到现有的类加载器,但它使用它来将其添加到新的类加载器。没有理由这样做 - 您可以使用 the URLClassLoader constructor

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

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

【讨论】:

  • 这里的问题是“我怎样才能使反射在 JDK 16 及更高版本上工作?”:你的答案是否解决了问题?
  • @Chris F 您是否有代码示例来说明上述答案为何不起作用,以及何时需要问题代码? addUrl 的源代码更新了与向构造函数提供 url 时相同的成员,因此这个答案看起来很有帮助。
猜你喜欢
  • 2014-08-20
  • 2019-09-06
  • 2016-04-07
  • 2019-09-11
  • 1970-01-01
  • 2015-03-04
  • 1970-01-01
  • 2022-06-18
  • 2021-11-17
相关资源
最近更新 更多