我在使用 Java 类加载器方面遇到了一些困难,也许有人可以解释一下这一点。我把问题的本质提炼如下:
共有三个类 - ClassLoaderTest
, LoadedClass
和LoadedClassDep
。他们都走在不同的道路上。
ClassLoaderTest
实例化一个新的 URLClassLoader
-myClassLoader
,用剩余两个类的路径和它自己的类加载器(即应用程序类加载器)作为父类来启动它。然后它使用 Class.forName("com.example.LoadedClass", true, myClassLoader)
加载LoadedClass
通过反射(reflection)。 LoadedClass
导入 LoadedClassDep
。如果我运行上面的代码,使用:
java -cp /path/to/the/ClassLoaderTest ClassLoaderTest "/path/to/LoadedClass" "/path/to/LoadedClassDep"
并使用命令行参数来启动 URLClassLoader
一切正常。使用静态初始化程序,我确认这两个类已加载 URLClassLoader
的实例。 。
但是,如果我这样做,这就是问题所在:
java -cp /path/to/the/ClassLoaderTest:/path/to/the/LoadedClass ClassLoaderTest "/path/to/LoadedClassDep"
无法加载 LoadedClassDep ( ClassNotFoundException
)。 LoadedClass
已正确加载,但带有 sun.misc.Launcher$AppClassLoader
,而不是 URLClassLoader
!
看起来,由于应用程序类加载器能够加载 LoadedClass
它还尝试加载 LoadedClassDep
,忽略 URLClassLoader
.
这是完整的源代码:
package example.bc;
public class ClassloaderTest {
public static void main(String[] args) {
new ClassloaderTest().run(args);
}
private void run(String[] args) {
URLClassLoader myClasLoader = initClassLoader(args);
try {
Class<?> cls = Class.forName("com.example.bc.LoadedClass", true, myClasLoader);
Object obj = cls.newInstance();
cls.getMethod("call").invoke(obj);
} catch (Exception e) {
e.printStackTrace();
}
}
private URLClassLoader initClassLoader(String[] args) {
URL[] urls = new URL[args.length];
try {
for (int i = 0; i < args.length; i++) {
urls[i] = new File(args[i]).toURI().toURL();
}
} catch (MalformedURLException e) {
e.printStackTrace();
}
return new URLClassLoader(urls, getClass().getClassLoader());
}
}
package com.example.bc;
import com.bc.LoadedClassDep;
public class LoadedClass {
static {
System.out.println("LoadedClass " + LoadedClass.class.getClassLoader().getClass());
}
public void call() {
new LoadedClassDep();
}
}
package com.bc;
public class LoadedClassDep {
static {
System.out.println("LoadedClassDep " + LoadedClassDep.class.getClassLoader().getClass());
}
}
我希望我已经说得足够清楚了。我的问题是,我只知道 ClassLoadeTest
的路径在编译时,我必须在运行时对其他路径使用字符串。那么,有什么想法可以让第二种情况发挥作用吗?
最佳答案
我希望应用程序类加载器在第二种情况下加载 LoadedClass
,因为类加载器最初委托(delegate)给其父级 - 这是 standard behaviour 。在第二种情况下,LoadedClass
位于父级的类路径上,因此它会加载该类,而不是放弃并让 URLClassLoader
尝试。
然后,应用程序类加载器尝试加载 LoadedClassDep
,因为它是在 LoadedClass
中直接导入和引用的:
public void call() {
new LoadedClassDep();
}
如果需要在运行时动态且独立地加载这些类,则不能通过这种方式在它们之间进行直接引用。
也可以更改尝试类加载器的顺序 - 请参阅 Java classloaders: why search the parent classloader first?对此进行一些讨论。
关于java - 链式类加载器难题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9161133/