Java 如何控制哪个类加载器加载一个类?
目前的情况并不像标题所显示的那么简单 Java 1.6_17通过JWS运行 我有一个类,比如说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,我在
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文件
真的很遗憾,这就是问题所在。希望这能像它给我们带来的那样,为其他人节省无尽的沮丧时间。“从错误的第三方库”最终的最佳策略是取代它