Java 如何控制哪个类加载器加载一个类?

Java 如何控制哪个类加载器加载一个类?,java,classloader,jnlp,java-web-start,dynamic-class-loaders,Java,Classloader,Jnlp,Java Web Start,Dynamic Class Loaders,目前的情况并不像标题所显示的那么简单 Java 1.6_17通过JWS运行 我有一个类,比如说MyClass,它的一个实例成员变量是一个错误的第三方库中的类型,在类初始化过程中,它会使用class.forName(String)动态地加载它自己的一些类。在其中一种情况下,它会动态调用:Class.forName(“foo/Bar”)。该类名不遵循二进制名称的JLS,最终导致java.lang.NoClassDefFoundError:foo/Bar 我们有一个自定义的ClassLoader,我在

目前的情况并不像标题所显示的那么简单

Java 1.6_17通过JWS运行

我有一个类,比如说
MyClass
,它的一个实例成员变量是一个错误的第三方库中的类型,在类初始化过程中,它会使用
class.forName(String)
动态地加载它自己的一些类。在其中一种情况下,它会动态调用:
Class.forName(“foo/Bar”)
。该类名不遵循二进制名称的JLS,最终导致
java.lang.NoClassDefFoundError:foo/Bar

我们有一个自定义的
ClassLoader
,我在
ClassLoader.findClass(String)
ClassLoader.loadClass(String)
中添加了一个清理方法,解决了这个问题

我可以称之为:
myCustomClassLoader.findClass(“foo/Bar”)

这样就可以毫无问题地加载类。但是,即使我提前加载了类,我仍然会在以后得到异常。这是因为在初始化
MyClass
时,引用了
Bar
,他们的代码最终在某个静态块中调用了
Class.forName(“foo/Bar”)
。如果它试图使用的类加载器是我的自定义类加载器,这实际上是可以的。但事实并非如此。是
com.sun.jnlp.JNLPClassLoader
没有这样的卫生功能,因此我的问题就来了

我已经确保
Thread.currentThread().getContextClassLoader()
设置为我的自定义类加载器。但这(如你所知)没有效果。我甚至把它设置为我在
main()
中做的第一件事,因为我读了一些东西,
MyClass.class.getClassLoader()
-是JNLPClassLoader。如果我能强迫它不是JNLPClassLoader,而是使用我的,问题就解决了

如何通过类初始化期间的静态class.forName(“foo/Bar”)调用控制使用哪个类加载器加载类?我相信如果我能强制
MyClass.class.getClassLoader()
返回我的自定义类加载器,我的问题就会得到解决

如果有人有想法,我愿意接受其他选择


TL;DR:请帮助我强制第三方库中由
MyClass
引用的所有
Class.forName(String)
调用使用我选择的类加载器。

这让我想起了10年前读到的一篇关于Java中类加载安排的文章。它还在那里

这篇文章不会直接回答你的问题,但它可能有助于理解你的问题。您需要通过自定义类加载器加载
MyClass
,并胜过默认的类加载行为,即首先将类加载委托给父类加载器,只有在失败时才尝试加载类

允许
MyClass
由您以外的类加载器加载,将存储从实例化类到该类加载器的关系(通过
getClassLoader
),并使Java使用该其他类加载器尝试发现任何引用的类,通过类加载器层次结构和委托模型,可以有效地绕过自定义类加载器。如果类加载器定义了
MyClass
,您将有第二次机会

这听起来像是一项类似于
URLClassLoader
的工作,它覆盖了
loadClass
,并超越了驻留在jar中的类的委托模型。您可能希望使用引导方法(正如Thomas在上面的评论中所建议的那样)强制通过自定义类加载器加载单个入口点类,并拖动所有其他入口点类

同样有用的是同一个人,他警告你Class.forName的警告。这也可能会影响你的课堂安排


我希望这有助于并证明信息丰富。在任何情况下,这听起来像是一个很难破解的解决方案,随着代码的发展,很容易打破。

< P>如果你修补了<代码> ClassLoader < /Case>自己,那么你可以考虑改写库字节码本身——用正确的值替换“Fo/bar”常量,然后,您根本不需要定制进一步的类加载


你可以在运行时或事先做这件事。

我认为每个人都在回答这个问题上做了很好的尝试。然而,结果是我误诊了这个问题

我让一位同事来处理这个问题,并让他获得一个启用了调试标志的JDK,这样我们就可以调试
JNLPClassLoader
,看看发生了什么,因为我已经尝试了这里的所有建议

我们最终得到了OpenJDK,因为从头开始重新编译JDK完全是一场噩梦(我们尝试过)。在让OpenJDK使用我们的产品并通过
JNLPClassLoader
进行调试之后,发现它仍然在使用几个月前的一个非常旧的.jnlp,它的资源路径错误,因此无法找到该类

我们不明白为什么它仍然使用古老的.jnlp,尽管我们已经多次使用正确的.jnlp正确地重新部署了服务器,并且在运行时客户端应用程序中反映了这两者之间的大量代码更改

事实证明,在客户机上,Java缓存了.jnlp文件。即使您的应用程序发生更改并重新下载您的应用程序,它仍然不会出于任何原因重新下载新的.jnlp。因此,它将使用所有新代码,但使用cached.jnlp查找资源/类路径

如果您运行:
javaws-卸载
然后在客户端计算机上清除.jnlp缓存,下次将使用正确的.jnlp文件

真的很遗憾,这就是问题所在。希望这能像它给我们带来的那样,为其他人节省无尽的沮丧时间。

“从错误的第三方库”最终的最佳策略是取代它