Загрузчики классов связаны отношениями родитель/потомок. У каждого загрузчика классов, за исключением первичного, имеется родительский загрузчик классов. Предполагается, что загрузчик классов дает шанс своему родителю загружать любой нужный класс и загружает его сам только в том случае, если этого не может сделать родитель. Например, если системный загрузчик классов запрашивает загрузку системного класса(например, java.util.arrayList), то прежде он предлагает загрузить его расширенному загрузчику классов.
Расширенный загрузчик классов, в свою очередь, предлагает сделать это первичному загрузчику классов. Наконец, первичный загрузчик классов находит и загружает класс из файла rt.jar при этом все другие загрузчики классов прекращают дальнейший поиск.
Некоторые программы имеют основанную на подключаемых модулях архитектуру, в которой определенные части кода упаковываются в виде дополнительных необязательных модулей. Если подключаемые модули упаковываются в виде файлов JAR, классы этих модулей могут загружаться просто с помощью экземпляра URLClassLoader:
1 2 3 |
URL url = new URL("file:///path/to/plugin.jar"); URLClassLoader pluginLoader = new URLClassLoader(new URL[] { url }); Class<?> cl = pluginLoader.loadClass("mypackage.MyClass"); |
Здесь, поскольку никакой родитель не был указан в конструкторе URLClassLoader, в роли родителя pluginLoader будет выступать системный загрузчик классов. Общий вид иерархии загрузчиков классов показан на рисунке 1.
В большинстве случае разработчику не приходится волноваться об их иерархии загрузчиков классов. Как правило, классы загружаются потому, что этого требуют другие классы, и этот процесс является абсолютно прозрачным для разработчика.
Однако иногда разработчику все-таки бывает нужно вмешиваться и указывать загрузчик класса. Рассмотрим следующий пример.
- В коде приложения содержится вспомогательный метод, вызывающий Class.forName(classNameString).
- Этот метод вызывается из класса подключаемого модуля.
- Параметр classNameString указывает на класс, содержащийся в plugin.jar.
Автор подключаемого модуля имеет все основания ожидать, что этот класс будет загружаться. Однако класс вспомогательного метода будет загружать системный классов, а это именно тот загрузчик, который необходим Class.forName. Это значит, что классы в plugin.jar видны не будут. Данный феномен называется инверсией загрузчиков классов(classloader inversion).
Для устранения этой проблемы необходимо, чтобы вспомогательный метод использовал правильный загрузчик классов. Это может потребовать либо указания загрузчика классов в виде параметра, либо установки нужного загрузчика в качестве контекстного загрузчика классов(context class loader) текущего потока. Вторая стратегия применяется очень во многих инфраструктурах, например в инфраструктуре JAXP и инфраструктуре JNDI.
Рисунок 1. Иерархия загрузчиков классов Java.
У каждого потока имеется ссылка на загрузчик классов, называемый контекстным загрузчиком классов. Контекстным загрузчиком главного потока является системный загрузчик классов. При создании нового потока ему в качестве контекстного загрузчика назначается контекстный загрузчик классов того потока, который его создает.
Следовательно, если ничего не делать, всем потокам в качестве контекстного загрузчика будет назначаться системный загрузчик классов.
Однако также существует и возможность указывать любой другой загрузчик классов путем вызова:
1 2 |
Thread t = Thread.currentThread(); t.setContextClassLoader(loader); |
В рассматриваемом примере это означает, что вспомогательный метод тогда может извлекать контекстный загрузчик классов:
1 2 3 |
Thread t = Thread.currentThread(); ClassLoader loader = t.getContextClassLoader(); Class cl = loader.loadClass(className); |
Теперь остается только вопрос о том, когда этому контекстному загрузчику классов должен назначаться загрузчик класса подключаемого модуля. Данное решение зависит уже от самого разработчика приложения.
Обычно неплохим вариантом считается делать так, чтобы контекстный загрузчик классов устанавливался при вызове метода класса подключаемого модуля, который был загружен с помощью другого загрузчика классов. В качестве альтернативного варианта, однако, можно делать и так, чтобы контекстный загрузчик классов устанавливала вызывающая программа вспомогательного метода.
В случае написания метода, загружающего класса по имени, рекомендуется предоставлять вызывающей программе возможность выбирать между передачей явного загрузчика классов и применением контекстного загрузчика классов. Просто использовать загрузчик класса метода не стоит.
Сфера использования панорам высокого разрешения достаточно разнообразна. Создание панорамы имеет одну цель – максимально детализировать объект съемки.