Я использую сторонний код, который при указании аргумента командной строки '-classpath' не устанавливает java.class.path, а вместо этого просто создает загрузчик классов, добавляет все URL-адреса для элементов в командной строке, указанный путь к классам, в загрузчик классов , а затем устанавливает его как загрузчик классов контекста. В классе плагина для этого кода, который я написал, я получаю экземпляр этого загрузчика классов, и мне нужно каким-то образом использовать его, чтобы вернуть базовый путь к классам, чтобы я мог использовать его при вызове JavaCompiler.getTask (... ) и компилировать на лету другой код. Однако, похоже, в любом случае нет возможности получить ClassPath из ClassLoader, и поскольку java.class.path не установлен, я не могу получить доступ к базовому пути к классам, с которым приложение было первоначально вызвано ... Есть идеи?
Как получить путь к классам из загрузчика классов?
Ответы (5)
Если загрузчик классов использует URL-адреса, это должен быть URLClassloader
. У вас есть доступ к URL-адресам, которые определяют путь к классам для него вместе с его родительским ClassLoader
.
Чтобы получить URL-адреса, просто сделайте следующее:
((URLClassLoader) (Thread.currentThread().getContextClassLoader())).getURLs()
Самый чистый способ перечислить путь к классам сегодня - использовать библиотеку 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();
ClassGraph
и URLClassloader.getURLs()
для приложения с загрузкой Spring со встроенным Tomcat. ClassGraph игнорирует папку /BOOT-INF/classes
, а также некоторую банку в <JRE_HOME>/lib/ext
, а getURLs()
игнорирует папку tomcat-docbase.nnn
, созданную в моей системной временной папке.
- person Pino; 25.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");
Если другие ответы не работают, попробуйте следующее:
ClassLoader cl = ClassLoader.getSystemClassLoader();
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url: urls) {
System.out.println(url.getFile());
}
Перетащите этот код на пустую страницу 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>