java - example - spring security module
¿Cómo obtener classpath de classloader? (5)
ACTUALIZACIÓN: Mi respuesta original a continuación es lamentablemente inadecuada, ahora que he pasado tres años desarrollando FastClasspathScanner y he tenido una gran cantidad de informes de errores archivados sobre ciertos entornos classpath que no funcionan con esta biblioteca. FastClasspathScanner ahora maneja numerosos mecanismos complejos de especificación de classpath . Incluso el simple hecho de encontrar el classpath puede ser increíblemente complicado en el caso general (y mucho menos escanearlo), ya que hay muchas maneras de agregar archivos jar y directorios al classpath.
Por un lado, el código que proporcioné a continuación solo maneja URLClassLoader
, y muchos de los principales entornos de tiempo de ejecución y contenedores no lo extienden, implementan su propio cargador de clases desde cero. Pero se vuelve mucho más complicado que esto en el caso de Java 9+, ya que aunque la ruta de clase tradicional todavía existe, todo en el futuro se moverá hacia el uso de la ruta del módulo, no la ruta de clase. Los módulos tienen URL, pero son URL "jrt:/"
, no URL "file:/"
, y las URL del módulo no incluyen una ruta de archivo, solo el nombre del módulo, por lo que en general no se puede encontrar módulo en disco. Su única opción es utilizar el sistema de módulos (muy encapsulado) para trabajar con los módulos. stackoverflow.com/questions/41932635/… .
FastClasspathScanner maneja numerosos mecanismos de especificación de classpath complejos, por lo que no es necesario reinventar la rueda. Puede obtener una lista de las entradas de la ruta de clase de FastClasspathScanner ; esto le ahorrará la molestia de intentar que algo funcione con todos los diversos mecanismos de especificación de ruta de clase que encuentra en la naturaleza. (Disculpas si ese último enlace se rompe: la API y los documentos para FCS cambiarán pronto).
-
[Antigua respuesta - obsoleta:]
Las otras respuestas son correctas en la mayoría de las situaciones, pero se complica más que en algunas configuraciones: por ejemplo, Maven , Tomcat y JUnit tienen su propio soporte de classpath y no usan el classpath del sistema.
Hasta ahora, este es el sistema más completo que he logrado crear (este código es de mi proyecto Fast Classpath Scanner ):
/** The unique elements of the classpath, as an ordered list. */
private final ArrayList<File> classpathElements = new ArrayList<>();
/** The unique elements of the classpath, as a set. */
private final HashSet<String> classpathElementsSet = new HashSet<>();
/** Clear the classpath. */
private void clearClasspath() {
classpathElements.clear();
classpathElementsSet.clear();
}
/** Add a classpath element. */
private void addClasspathElement(String pathElement) {
if (classpathElementsSet.add(pathElement)) {
final File file = new File(pathElement);
if (file.exists()) {
classpathElements.add(file);
}
}
}
/** Parse the system classpath. */
private void parseSystemClasspath() {
// Look for all unique classloaders.
// Keep them in an order that (hopefully) reflects the order in which class resolution occurs.
ArrayList<ClassLoader> classLoaders = new ArrayList<>();
HashSet<ClassLoader> classLoadersSet = new HashSet<>();
classLoadersSet.add(ClassLoader.getSystemClassLoader());
classLoaders.add(ClassLoader.getSystemClassLoader());
if (classLoadersSet.add(Thread.currentThread().getContextClassLoader())) {
classLoaders.add(Thread.currentThread().getContextClassLoader());
}
// Dirty method for looking for any other classloaders on the call stack
try {
// Generate stacktrace
throw new Exception();
} catch (Exception e) {
StackTraceElement[] stacktrace = e.getStackTrace();
for (StackTraceElement elt : stacktrace) {
try {
ClassLoader cl = Class.forName(elt.getClassName()).getClassLoader();
if (classLoadersSet.add(cl)) {
classLoaders.add(cl);
}
} catch (ClassNotFoundException e1) {
}
}
}
// Get file paths for URLs of each classloader.
clearClasspath();
for (ClassLoader cl : classLoaders) {
if (cl != null) {
for (URL url : ((URLClassLoader) cl).getURLs()) {
if ("file".equals(url.getProtocol())) {
addClasspathElement(url.getFile());
}
}
}
}
}
/** Override the system classpath with a custom classpath to search. */
public FastClasspathScanner overrideClasspath(String classpath) {
clearClasspath();
for (String pathElement : classpath.split(File.pathSeparator)) {
addClasspathElement(pathElement);
}
return this;
}
/**
* Get a list of unique elements on the classpath (directories and files) as File objects, preserving order.
* Classpath elements that do not exist are not included in the list.
*/
public ArrayList<File> getUniqueClasspathElements() {
return classpathElements;
}
Estoy usando un código de terceros que cuando se le da un argumento de línea de comando ''-classpath'' no establece java.class.path, pero en su lugar solo crea un cargador de clases, agrega todas las direcciones URL de los elementos en la línea de comandos especificada classpath al cargador de clases , y luego lo establece para ser el cargador de clases de contexto. En una clase de complemento a este código que he escrito, obtengo una instancia de este cargador de clases, y de alguna manera necesito usarlo para recuperar el classpath subyacente, de modo que pueda usarlo en una invocación de JavaCompiler.getTask (... ) y compilar algún otro código sobre la marcha. Sin embargo, no parece haber ninguna forma de obtener el ClassPath del ClassLoader, y como java.class.path no está configurado, parece que no puedo acceder al classpath subyacente con el que se invocó inicialmente la aplicación con ... ¿Alguna idea?
Coloque este código en una página jsp vacía para ver la jerarquía de ClassLoader y los tarros asociados cargados en cada nivel.
El método de visita () a continuación también se podría utilizar por sí solo.
<%!
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>
En caso de que otras respuestas no funcionen, intente esto:
ClassLoader cl = ClassLoader.getSystemClassLoader();
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url: urls) {
System.out.println(url.getFile());
}
Para futuras referencias, en caso de que necesite pasar la ruta de la clase a 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");
Si el cargador de clases usa URL, debe ser un URLClassloader
. A lo que tiene acceso es a las URL que definen la ruta de clase para él junto con su ClassLoader
principal.
Para obtener las URL, simplemente haga lo siguiente:
((URLClassLoader) (Thread.currentThread().getContextClassLoader())).getURLs()