menu

Управление переменной classpath с помощью JWhich

Управление переменной classpath с помощью JWhich

 
( Майк Кларк )

Управление classpath и загрузкой классов.

Tips 'N Tricks
 

Рано или поздно разработчики сталкиваются с трудностями при установке в Java переменной classpath. Не всегда понятно, какой класс загрузит загрузчик, особенно когда путь к классам, используемым приложением наводняется ссылками на директории и файлы. В этой статье мы представим читателю средство отображения абсолютных путей всех загруженных классов.

Основы Classpath

Виртуальная машина Java (JVM) использует загрузчик классов по принципу использования классов приложением по требованию. Переменная средыCLASSPATH содержит список путей, где загрузчик классов может найти классы библиотек и пользовательские классы. Помимо этого можно указать пути при загрузке ключом -classpath командной строки JVM, который используется вместо переменной CLASSPATH.

В составе переменной могут находиться директории, которые содержат файлы классов, не принадлежащих библиотекам, корневые директории классов библиотек или файлы архивов (такие как .zip или .jar), содержащих классы. Составляющие классового пути могут быть разделены двоеточиями на системах тип Uni и точками с запятыми в системах MS Windows.

Загрузчики классов поддерживают представительную иерархию, при которой каждый загрузчик подчинен родительскому загрузчику. Когда загрузчику требуется найти класс, он прежде всего направляет запрос своему родительскому загрузчику, а затем пытается искать класс самостоятельно. Стандартным загрузчиком в JDK и JRE является системный загрузчик, который загружает классы и библиотеки, используя переменную среды CLASSPATH или ключ -classpath командной строки JVM. Для загрузки классов, которые используют механизм расширений Java, системный загрузчик обращается к классам расширения. Загрузчик классов расширения обращается к стартовому загрузчику классов (и на этом цепь заканчивается!) для загрузки классов ядра JDK.

Для управления способом динамической загрузки классов в JVM надо разработать свой специализированный загрузчик классов. Например, большинство механизмов работы сервлетов используют свои динамические загрузчики измененных классов в составе сервлета в директориях, указываемых специальным классовым путем.

Важным моментом, о котором многие узнают слишком поздно, является то, что загрузчик классов загружает их в порядке следования в переменной классового пути. Начиная с первого пути в этой переменной, загрузчик просматривает все указанные директории или архивы, пытаясь найти класс, который надо загрузить. Загружается первый же класс, который имеет подходящее имя и все оставшиеся части классового пути оставляются без внимания.

Звучит просто, не так ли?

Хитрости классовых путей

И новички программирования на Java, и ветераны равны в том, что все они (как правило в худший из возможных моментов) становились жертвой хитростей классовой переменной, признаются они в этом или нет. С ростом количества импортируемых библиотек и пользовательских классов в приложении и соответствующим удлинением классового пути, последовательность загрузки становится очень сложно проследить. В особенности это касается момента, когда приходится хранить копии классов. Следует помнить, что загрузчик классов использует первый попавшийся класс с подходящим именем и аккуратно спрячет все остальные классы с тем же именем или низшим приоритетом.

Попасться на хитрости классового пути очень легко. После длительных усилий над разогревшейся клавиатурой можно добавить в путь директорию для использования свежей и наилучшей версии класса в приложении, забыв о существовании другой версии этого класса в директории высшего приоритета, отмеченной в том же классовом пути. И все!

JWhich: простой инструмент для работы с классовым путем

Проблема приоритетов объявления списков путей не ограничивается классовым путем Java. За примером не надо далеко ходить, можно вспомнить легендарных гигантов программной индустрии. Команда which операционной системы Unix отображает полное имя команды, которая выполнится, если вписать в командной строке заданное имя. Она просматривает целиком переменную среды PATH, чтобы найти первое появление команды. По подобию построим инструмент, который будет отображать полный путь для загружаемого класса.

В следующем примере класс JWhich используется для отображения абсолютного пути впервые встречающегося классаcom.clarkware.ejb.ShoppingCartBean, который и будет использоваться загрузчиком классов, который будет помещен в директорию:

    > java JWhich com.clarkware.ejb.ShoppingCartBean

     Class 'com.clarkware.ejb.ShoppingCartBean' found in
     '/home/mclark/classes/com/
clarkware/ejb/ShoppingCartBean.class'

В следующем примере класс JWhich используется для отображения абсолютного пути впервые встречающегося класса javax.servlet.http.HttpServlet, который будет использоваться загрузчиком классов, который будет помещен в директорию:

   > java JWhich javax.servlet.http.HttpServlet

     Class 'javax.servlet.http.HttpServlet' found in
     'file:/home/mclark/lib/servlet.jar!/
javax/servlet/http/HttpServlet.class'

Как работает JWhich

Чтобы точно определить, какой класс будет загружен первым, надо мыслить также, как загрузчик классов. Это не так сложно, как может показаться на первый взгляд — надо лишь спросить его! Ниже следует соответствующий код JWhich. Полный код помещен в Ресурсах.

1: public class JWhich {
2:
3: /**
4: * Prints the absolute pathname of the class file
5: * containing the specified class name, as prescribed
6: * by the current classpath.
7: *
8: * @param className Name of the class.
9: */
10: public static void which(String className) {
11:
12: if (!className.startsWith("/")) {
13: className = "/" + className;
14: }
15: className = className.replace('.', '/');
16: className = className + ".class";
17:
18: java.net.URL classUrl =
19: new JWhich().getClass().getResource(className);
20:
21: if (classUrl != null) {
22: System.out.println("\nClass '" + className +
23: "' found in \n'" + classUrl.getFile() + "'");
24: } else {
25: System.out.println("\nClass '" + className +
26: "' not found in \n'" +
27: System.getProperty("java.class.path") + "'");
28: }
29: }
30:
31: public static void main(String args[]) {
32: if (args.length > 0) {
33: JWhich.which(args[0]);
34: } else {
35: System.err.println ("Usage: java JWhich <classname>");
36: }
37: }
38: }

Сначала надо обработать имя класса, чтобы его принял загрузчик классов (строки 12-16). Подставляя "/" к имени класса, заставим загрузчик классов дословно сравнивать имя класса с классовым путем, а не пытаться неявно подставить имя библиотеки вызывающего класса. Преобразование каждой "." в "/" делает имя класса правильным именем ресурса URL, необходимого для загрузчика классов.

Затем, у загрузчика классов запрашивается (строки 18-19) ресурс, соответствующий правильно отформатированному имени класса. Каждый объект типаClass имеет ссылку на объект ClassLoader, который загрузил его, поэтому здесь опрашивается загрузчик, который загрузил сам класс JWhich. МетодClass.getResource() в действительности обращается к загрузчику классов, который загрузил сам этот класс, и возвращает URL ресурса файла класса, илиnull, если ресурс файла класса с указанным именем класса не был найден по актуальному классовому пути.

Наконец, если файл класса с заданным именем был найден, отображается абсолютный путь к этому файлу, (строки 21-24). Для отладки можно получить значение системного свойства java.class.path, чтобы отобразить текущий классовый путь (строки 24-28), если в нем не был найден файл класса.

Легко видеть, что этот простой кусок кода можно вызывать из сервлета Java, используя классовый путь механизма сервлета, или из боба предприятия (EJB) для классового пути сервера EJB. Если класс JWhich загружается, например, специализированным загрузчиком в механизме сервлета, то для поиска классов будет использоваться механизм поиска этого сервлета. Если загрузчик классов в механизме сервлета не может найти класс, он обратится к загрузчику верхнего уровня. В общем, когда класс JWhich загружается загрузчиком классов, он может найти все классы, загруженные этим загрузчиком или породившими его загрузчиками.

Заключение

Группы новостей Java и списки рассылки забиты вопросами про классовый путь. Нам хотелось бы сократить разрыв с начинающими разработчиками , чтобы продолжать работать над высшими уровнями абстракции. JWhich — простое и мощное средство освоения классового пути Java к любой среде.

Об авторе

Майк Кларк — независимый консультант Clarkware Consulting, специализирующийся на архитектуре, дизайне и разработке на Java с использованием технологий J2EE. Он закончил недавно разработку и внедрение сервера XML для обмена бизнеса-на-бизнес (B2B), а теперь консультирует проект построения продукта упроавления производительностью на J2EE .

Ресурсы

Reprinted with permission from JavaWorld magazine. Copyright © ITworld.com, Inc., an IDG Communications company.
View the original article at: http://www.javaworld.com/ javatips/jw-javatip105.html



Source: http://www.javaworld.com/ javatips/jw-javatip105.html
Category: Java | Added by: tsvetkov (23.01.2009)
Views: 1493 | Rating: 0.0/0