链式类加载器难题

时间:2012-02-06 13:54:41

标签: java classloader

我在使用Java类加载器时遇到了一些困难,也许有人可以对此有所了解。我已将问题的本质提取到以下内容:

有三个类 - ClassLoaderTestLoadedClassLoadedClassDep。他们都在不同的道路上。

ClassLoaderTest实例化一个新的URLClassLoader - myClassLoader,用其余两个类的路径引导它,并将它自己的类加载器(即应用程序类加载器)作为父类。然后,它使用Class.forName("com.example.LoadedClass", true, myClassLoader)通过反射加载LoadedClassLoadedClass导入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的路径,我必须在运行时为其他路径使用字符串。那么,任何想法如何使第二个场景有效?

1 个答案:

答案 0 :(得分:1)

我希望应用程序类加载器在第二种情况下加载LoadedClass,因为类加载器最初委托给它们的父级 - 这是standard behaviour。在第二种情况下,LoadedClass位于父类的路径上,因此它加载类而不是放弃并让URLClassLoader尝试。

然后,应用程序类加载器会尝试加载LoadedClassDep,因为它是在LoadedClass中直接导入和引用的:

public void call() {
    new LoadedClassDep();
}

如果需要在运行时动态且独立地加载这些类,则不能以这种方式直接引用它们。

也可以更改类加载器的尝试顺序 - 有关此问题的讨论,请参阅Java classloaders: why search the parent classloader first?