2012-02-06 4 views
1

У меня возникли проблемы с загрузчиками классов Java, возможно, кто-то может пролить свет на это. Я выделил суть проблемы для следующего:Chained classloader conundrum

Существует три класса - ClassLoaderTest, LoadedClass и LoadedClassDep. Все они на разных путях.

ClassLoaderTest инициализирует новый URLClassLoader - myClassLoader, заливка его пути к остальным двум классам и его собственный загрузчик классов (то есть загрузчиком классов приложений) в качестве родителя. Затем он использует Class.forName("com.example.LoadedClass", true, myClassLoader) для загрузки LoadedClass через отражение. 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 во время компиляции, я должен использовать строки во время выполнения для других путей. Итак, любые идеи о том, как создать второй сценарий?

ответ

1

Я бы ожидал, что загрузчик классов приложений загрузит LoadedClass во втором случае, так как загрузчики классов делегируют их родителям изначально - это standard behaviour. Во втором случае LoadedClass находится на пути к родительскому классу, поэтому он загружает класс вместо того, чтобы сдаваться и разрешать попытку URLClassLoader.

загрузчиком классов приложений затем пытается загрузить LoadedClassDep, потому что он импортируется и ссылки непосредственно в LoadedClass:

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

Если вам нужно загрузить эти классы динамически и независимо друг от друга во время выполнения, вы не можете иметь прямые ссылки между ними таким образом.

Также можно изменить порядок, в котором разбираются загрузчики классов - см. Java classloaders: why search the parent classloader first? для обсуждения этого вопроса.

+0

Это кажется правильным. Я надеялся, что, хотя делегат 'URLClassloader' для родителя, загруженный класс будет по-прежнему иметь его как загрузчик классов, так как именно там был инициирован вызов. Но нет, это AppClassLoader с тех пор. Спасибо –

+0

Вот что я сделал - я привязал «URLClassloader», чтобы загружать только «LoadedClass» и «LoadedClassDep» (которых в реальной программе много). Поэтому он делегирует родительскому загрузчику классов, если только он не является одним из вышеперечисленных, и в этом случае он загружает их самостоятельно.Таким образом, «LoadedClass» имеет привязанный к нему загрузчик классов и запрашивает _it_ для загрузки своих зависимостей. –