Как получить путь к классам из загрузчика классов?

Я использую сторонний код, который при указании аргумента командной строки '-classpath' не устанавливает java.class.path, а вместо этого просто создает загрузчик классов, добавляет все URL-адреса для элементов в командной строке, указанный путь к классам, в загрузчик классов , а затем устанавливает его как загрузчик классов контекста. В классе плагина для этого кода, который я написал, я получаю экземпляр этого загрузчика классов, и мне нужно каким-то образом использовать его, чтобы вернуть базовый путь к классам, чтобы я мог использовать его при вызове JavaCompiler.getTask (... ) и компилировать на лету другой код. Однако, похоже, в любом случае нет возможности получить ClassPath из ClassLoader, и поскольку java.class.path не установлен, я не могу получить доступ к базовому пути к классам, с которым приложение было первоначально вызвано ... Есть идеи?


person Marcus Mathioudakis    schedule 23.07.2012    source источник


Ответы (5)


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

Чтобы получить URL-адреса, просто сделайте следующее:

((URLClassLoader) (Thread.currentThread().getContextClassLoader())).getURLs()
person Adel Boutros    schedule 23.07.2012
comment
Это решение больше не работает на Java 9, потому что загрузчик классов не может быть добавлен в URLClassLoader. - person G Quintana; 15.03.2018
comment
Дж. Кинтана верна. См. Мой ответ о сканировании пути к модулю Java 9+ здесь: stackoverflow.com/questions/41932635/ - person Luke Hutchison; 06.07.2018
comment
ClassCastException: невозможно преобразовать 'jdk.internal.loader.ClassLoaders $ AppClassLoader' в 'java.net.URLClassLoader' - person cdalxndr; 03.02.2021

Самый чистый способ перечислить путь к классам сегодня - использовать библиотеку ClassGraph (я являюсь автором). Обратите внимание, что старый ответ чтения свойства java.class.path или вызова ((URLClassLoader) (Thread.currentThread().getContextClassLoader())).getURLs() совершенно неадекватен, если вы хотите, чтобы ваш код был переносимым сегодня, потому что многие среды выполнения больше не используют java.class.path и / или их загрузчики классов не расширяют URLClassLoader, и / или они используйте какой-то неясный механизм для расширения пути к классам (например, свойство Class-Path: в файле манифеста jar), и / или ваш код может быть запущен как модуль в JDK 9+ (или ваш код будет запускаться в традиционном пути к классам в JDK9 +, но стандартные загрузчики классов JDK для традиционного пути к классам даже не расширяют URLClassLoader).

ClassGraph автоматически обрабатывает огромное количество механизмов спецификации пути к классам и реализаций загрузчика классов. Для большинства поддерживаемых загрузчиков классов для ClassGraph был написан собственный код отражения для получения пути к классам из загрузчика классов (это необходимо, поскольку API ClassLoader не имеет стандартного механизма для получения пути к классам). Вы можете написать свой собственный код для этого, но, вероятно, он будет поддерживать только URLClassLoader без значительных усилий - так что, вероятно, лучше просто использовать ClassGraph, поскольку работа уже сделана за вас.

Чтобы получить путь к классам (и несистемные модульные jar-файлы, добавленные к пути к модулю), просто вызовите:

List<URI> classpath = new ClassGraph().getClasspathURIs();

Обратите внимание, что в Java 9+ модули (или jlink'd jar-файлы) могут отображаться в списке с jrt: URI, с которыми вы не можете многое сделать напрямую (кроме использования ClassGraph для чтения ресурсов и классов из них, поскольку ClassGraph может дополнительно используйте JPMS API для доступа к этим ресурсам и классам). Вы также можете использовать ClassGraph для перечисления или сканирования всех классов и / или всех ресурсов в пути к классам (см. вики-страницу ClassGraph < / а>).

В модульном проекте на Java 9+ вы также можете получить список _ 11_ объекты для видимых модулей в системе. Их можно получить, вызвав следующее (ModuleRef - это оболочка для ModuleReference, имеет обратную совместимость, поэтому вы можете скомпилировать свой код на JDK 7/8, но по-прежнему пользоваться функциями модуля на JDK 9+):

List<ModuleRef> modules =
    new ClassGraph()
        .enableSystemPackages() // Optional, to return system modules
        .getModules();

Или вы можете получить фактический путь к модулю, переданный в командную строку (--module-path, --patch-module, --add-exports и т. Д.), Вызвав следующее, вернув список _ 18_ объекты:

List<ModulePathInfo> modulePathInfo = new ClassGraph().getModulePathInfo();
person Luke Hutchison    schedule 03.08.2015
comment
Однозначно наиболее полный ответ и активно развивающийся, если вы следите за проектом. - person Michael McCallum; 21.08.2015
comment
Этот грязный метод, который ищет любые другие загрузчики классов в стеке вызовов, был именно тем, что мне было нужно! Большое спасибо! - person FelipeKunzler; 19.02.2016
comment
@ luke-hutchison, ваша библиотека спасает жизнь! Это лучшее решение для всех, кто хочет получить путь к классам для различных сред выполнения. - person Dan; 26.02.2016
comment
Я сравнил результат ClassGraph и URLClassloader.getURLs() для приложения с загрузкой Spring со встроенным Tomcat. ClassGraph игнорирует папку /BOOT-INF/classes, а также некоторую банку в <JRE_HOME>/lib/ext, а getURLs() игнорирует папку tomcat-docbase.nnn, созданную в моей системной временной папке. - person Pino; 25.11.2020
comment
@Pino Оба этих каталога имеют явную поддержку в ClassGraph, поэтому они должны работать. Не могли бы вы отправить отчет об ошибке на сайте ClassGraph на github? - person Luke Hutchison; 27.11.2020

Для справки в будущем, если вам нужно передать путь к классу ProcessBuilder:

StringBuffer buffer = new StringBuffer();
for (URL url :
    ((URLClassLoader) (Thread.currentThread()
        .getContextClassLoader())).getURLs()) {
  buffer.append(new File(url.getPath()));
  buffer.append(System.getProperty("path.separator"));
}
String classpath = buffer.toString();
int toIndex = classpath
    .lastIndexOf(System.getProperty("path.separator"));
classpath = classpath.substring(0, toIndex);
ProcessBuilder builder = new ProcessBuilder("java",
    "-classpath", classpath, "com.a.b.c.TestProgram");
person BJ Dela Cruz    schedule 12.10.2012

Если другие ответы не работают, попробуйте следующее:

ClassLoader cl = ClassLoader.getSystemClassLoader();
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url: urls) {
    System.out.println(url.getFile());
}
person Yavin5    schedule 21.05.2014

Перетащите этот код на пустую страницу jsp, чтобы просмотреть иерархию classLoader и связанные jar-файлы, загруженные на каждом уровне.

Метод visit (), приведенный ниже, также можно использовать отдельно

<%!
    public void visit(StringBuilder sb, int indent, ClassLoader classLoader) {
        if (indent > 20 || classLoader == null)
            return;
        String indentStr = new String(new char[indent]).replace("\0", "    ");
        sb.append("\n");
        sb.append(indentStr);
        sb.append(classLoader.getClass().getName());
        sb.append(":");
        if (classLoader instanceof java.net.URLClassLoader) {
            java.net.URL[] urls = ((java.net.URLClassLoader)classLoader).getURLs();
            for (java.net.URL url : urls) {
                sb.append("\n");
                sb.append(indentStr);
                sb.append(url);
            }
        }
        sb.append("\n");
        visit(sb, indent + 1, classLoader.getParent());
    }

%>

<%
StringBuilder sb = new StringBuilder();
visit(sb,1,this.getClass().getClassLoader());
%>
<pre>
<%=sb%>
</pre>
person Prashant Bhate    schedule 14.04.2016