У меня возникли проблемы с загрузчиками классов 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
во время компиляции, я должен использовать строки во время выполнения для других путей. Итак, любые идеи о том, как создать второй сценарий?
Это кажется правильным. Я надеялся, что, хотя делегат 'URLClassloader' для родителя, загруженный класс будет по-прежнему иметь его как загрузчик классов, так как именно там был инициирован вызов. Но нет, это AppClassLoader с тех пор. Спасибо –
Вот что я сделал - я привязал «URLClassloader», чтобы загружать только «LoadedClass» и «LoadedClassDep» (которых в реальной программе много). Поэтому он делегирует родительскому загрузчику классов, если только он не является одним из вышеперечисленных, и в этом случае он загружает их самостоятельно.Таким образом, «LoadedClass» имеет привязанный к нему загрузчик классов и запрашивает _it_ для загрузки своих зависимостей. –