Цитировать из ClassLoader JavaDoc :
Методы и конструкторы
объекты, созданные загрузчиком классов, могут
ссылки на другие классы. Определить
упомянутый класс (ы), Java
виртуальная машина вызывает loadClass
метод загрузчика классов, который
Первоначально создан класс.
Другими словами, когда вы загружаете класс, этот класс пытается загрузить другие классы через загрузчик классов, который его загрузил. В обычном приложении Java это системный загрузчик классов, который представляет путь к классу, передаваемый в JVM, или загрузчик загрузчика классов, используемый для загрузки среды выполнения JVM.
В зависимости от ваших потребностей, существует вариант Class.forName () , который принимает загрузчик классов в качестве аргумента. Если вы используете это для загрузки определенного класса, то ссылки в этом классе должны использовать указанный загрузчик классов.
Редактировать: я начал прослеживать ваш пример, но решил, что будет проще дать свой. Если вы собираетесь написать свой собственный загрузчик классов, я предлагаю начать с существующего URLClassLoader , потому что он обрабатывает множество закулисных вещей.
Итак, MyClassLoader
берет один каталог JARfile / и загружает классы только для этого каталога. Я переопределил три метода, вызываемых для загрузки класса, и просто регистрирую их вызов (используя System.err, потому что он не буферизует вывод, в отличие от System.out).
В моем примере используется библиотека, над которой я сейчас работаю; это было удобно, но вы можете выбрать любую нужную вам библиотеку , если ее еще нет в вашем classpath .
Метод main () загружает класс через MyLoader. Затем я вызываю метод для этого класса способом, который, как я знаю, генерирует исключение, которое также является частью библиотеки. Обратите внимание, что я вызываю метод с помощью отражения: поскольку библиотека отсутствует в моем пути к классам Eclipse, я не смог скомпилировать его с явной ссылкой.
Когда я запускаю эту программу (под Sun JDK 1.5 для Linux), я вижу много вызовов loadClass (), как для классов в моей библиотеке, так и для классов в пути к классам. Это ожидаемо: класс ParseUtil ссылается на множество других классов и будет использовать MyLoader (т. Е. Его загрузчик классов) для их загрузки. Для тех классов, которые MyLoader не может найти локально, он делегирует дерево загрузчика.
Исключение выдается, и когда я распечатываю его загрузчик классов, я вижу, что он совпадает с созданным мной экземпляром MyLoader. Я также распечатываю загрузчик для Exception.class, и он имеет значение null - что в JavaDoc для Class.getClassLoader () указывает на загрузчик загрузчика классов.
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
public class ClassLoaderExample
{
private static class MyClassLoader
extends URLClassLoader
{
public MyClassLoader(String path)
throws Exception
{
super(new URL[] { new File(path).toURL() });
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{
System.err.println("findClass(" + name + ")");
return super.findClass(name);
}
@Override
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
System.err.println("loadClass(" + name + "," + resolve + ")");
return super.loadClass(name, resolve);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException
{
System.err.println("loadClass(" + name + ")");
return super.loadClass(name);
}
}
public static void main(String[] argv)
throws Exception
{
ClassLoader myLoader = new MyClassLoader("/home/kgregory/Workspace/PracticalXml-1.1/target/classes/");
System.out.println("myLoader = " + myLoader);
Class<?> parseUtilKlass = myLoader.loadClass("net.sf.practicalxml.ParseUtil");
Method parseMethod = parseUtilKlass.getDeclaredMethod("parse", String.class);
try
{
parseMethod.invoke(null, "not at all valid XML");
}
catch (InvocationTargetException e)
{
Throwable ee = e.getCause();
System.out.println("exception:" + ee);
System.out.println("exception loader = " + ee.getClass().getClassLoader());
System.out.println("Exception.class loader = " + Exception.class.getClassLoader());
}
}
}
Редактировать # 2, основываясь на сегодняшних комментариях.
Ожидается, что загрузчик классов делегирует запросы своему родителю до того, как попытается выполнить сам запрос (это находится в ClassLoader JavaDoc). У этой практики есть несколько преимуществ, прежде всего то, что вы не будете непреднамеренно загружать несовместимые экземпляры одного и того же класса.
Загрузчики классов J2EE изменяют эту модель: загрузчик классов, используемый для загрузки WAR, попытается разрешить классы перед загрузчиком для содержащего EAR, который, в свою очередь, попытается разрешить классы до загрузчика классов контейнера. Цель здесь - изоляция: если и WAR, и его EAR содержат одну и ту же библиотеку, возможно, это связано с тем, что обеим версиям нужны разные версии (или у них небрежный процесс сборки). Даже в случае J2EE я считаю, что контейнерный загрузчик классов делегирует стандартным образом.