Encuentra desde dónde se carga la clase java

184

¿Alguien sabe cómo averiguar programáticamente de dónde carga Java Classloader realmente la clase?

A menudo trabajo en proyectos grandes donde el classpath se hace muy largo y la búsqueda manual no es realmente una opción. Recientemente tuve un problema en el que el cargador de clases estaba cargando una versión incorrecta de una clase porque estaba en el classpath en dos lugares diferentes.

Entonces, ¿cómo puedo hacer que el cargador de clases me diga de qué parte del disco proviene el archivo de clase real?

Editar: ¿Qué pasa si el cargador de clases realmente no puede cargar la clase debido a una discrepancia de versión (u otra cosa), ¿hay alguna forma de que podamos averiguar qué archivo está tratando de leer antes de leerlo?

luke
fuente
44
@JarrodRoberson No creo que esto deba considerarse un duplicado de stackoverflow.com/questions/11747833/… ya que esta pregunta se hizo en 2008 y esa pregunta se hizo en 2012
luke

Respuestas:

189

Aquí hay un ejemplo:

package foo;

public class Test
{
    public static void main(String[] args)
    {
        ClassLoader loader = Test.class.getClassLoader();
        System.out.println(loader.getResource("foo/Test.class"));
    }
}

Esta impreso:

file:/C:/Users/Jon/Test/foo/Test.class
Jon Skeet
fuente
32
Para reducir la escritura redundante, también se puede usar la versión más corta: Test.class.getResource("Test.class")que no repite el nombre del paquete.
meriton
1
¿Qué sucede si la clase se compila, por ejemplo, desde un archivo .groovy?
Ondra Žižka
35
@meriton: O, para sobrevivir a las refactorizaciones:Test.class.getResource(Test.class.getSimpleName() + ".class")
leonbloy
1
Por BouncyCastleProvidernombre completo del paquete, sin embargo se requiere.
Pavel Vlasov
3
Es posible getClassLoader()regresar null. Vea aquí una extensión de este método para manejar eso.
OldCurmudgeon
100

Otra forma de averiguar desde dónde se carga una clase (sin manipular la fuente) es iniciar Java VM con la opción: -verbose:class

jiriki
fuente
66
esto funcionó muy bien, y no tiene el problema de lidiar con clases con ClassLoader nulo
lexicalscope
2
@ries Si uno no necesita hacer esto programáticamente, este es definitivamente el camino a seguir, y resolvió mi problema. Sin embargo, el OP había preguntado específicamente cómo hacer esto programáticamente.
SantiBailors
80
getClass().getProtectionDomain().getCodeSource().getLocation();
Dave DiFranco
fuente
44
Sí, aunque no funciona con un administrador de seguridad instalado y sin los permisos necesarios.
Tom Hawtin - tackline el
1
FYI, NPE = Excepción de puntero nulo. HTH!
Evgeni Sergeev
Se prefiere este método siempre que tenga una referencia a una instancia, ya que puede cargar la misma clase desde dos ubicaciones diferentes.
Miguel Ping
1
Tampoco funciona cuando se llama desde un módulo Java 9+ (que, por supuesto, no podría haber sabido en 2008).
Jeff G
28

Esto es lo que usamos:

public static String getClassResource(Class<?> klass) {
  return klass.getClassLoader().getResource(
     klass.getName().replace('.', '/') + ".class").toString();
}

Esto funcionará dependiendo de la implementación de ClassLoader: getClass().getProtectionDomain().getCodeSource().getLocation()

Jevgeni Kabanov
fuente
17

La versión de Jon falla cuando el objeto ClassLoaderse registra como lo nullque parece implicar que fue cargado por Boot ClassLoader.

Este método trata ese problema:

public static String whereFrom(Object o) {
  if ( o == null ) {
    return null;
  }
  Class<?> c = o.getClass();
  ClassLoader loader = c.getClassLoader();
  if ( loader == null ) {
    // Try the bootstrap classloader - obtained from the ultimate parent of the System Class Loader.
    loader = ClassLoader.getSystemClassLoader();
    while ( loader != null && loader.getParent() != null ) {
      loader = loader.getParent();
    }
  }
  if (loader != null) {
    String name = c.getCanonicalName();
    URL resource = loader.getResource(name.replace(".", "/") + ".class");
    if ( resource != null ) {
      return resource.toString();
    }
  }
  return "Unknown";
}
OldCurmudgeon
fuente
5

Editar solo la primera línea: Main.class

Class<?> c = Main.class;
String path = c.getResource(c.getSimpleName() + ".class").getPath().replace(c.getSimpleName() + ".class", "");

System.out.println(path);

Salida:

/C:/Users/Test/bin/

Tal vez mal estilo pero funciona bien!

ecerer
fuente
3

Por lo general, no sabemos qué usar la codificación rígida. Podemos obtener className primero, y luego usar ClassLoader para obtener la URL de la clase.

        String className = MyClass.class.getName().replace(".", "/")+".class";
        URL classUrl  = MyClass.class.getClassLoader().getResource(className);
        String fullPath = classUrl==null ? null : classUrl.getPath();
Hongyang
fuente
Debe ser: URL classUrl = MyClass.class.getClassLoader (). GetResource ("/" + className);
Andrew Coates
MyClass.class es una parte importante: ¡getClass () puede devolver Proxy! Luego puede obtener un nombre como MyClass $$ EnhancerBySpringCGLIB $$ a98db882.class y una URL nula.
jalmasi
1

Manera simple:

System.out.println (java.lang.String.class.getResource (String.class.getSimpleName () + ". Class"));

Ejemplo de salida:

jar: archivo: / D: /Java/jdk1.8/jre/lib/rt.jar! /java/lang/String.class

O

String obj = "prueba simple"; System.out.println (obj.getClass (). GetResource (obj.getClass (). GetSimpleName () + ". Class"));

Ejemplo de salida:

jar: archivo: / D: /Java/jdk1.8/jre/lib/rt.jar! /java/lang/String.class

seduardo
fuente
0

Este enfoque funciona tanto para archivos como para frascos:

Class clazz = Class.forName(nameOfClassYouWant);

URL resourceUrl = clazz.getResource("/" + clazz.getCanonicalName().replace(".", "/") + ".class");
InputStream classStream = resourceUrl.openStream(); // load the bytecode, if you wish
Adán
fuente
-1

Suponiendo que está trabajando con una clase llamada MyClass, lo siguiente debería funcionar:

MyClass.class.getClassLoader();

Si puede o no obtener la ubicación en el disco del archivo .class depende del cargador de clases en sí. Por ejemplo, si está utilizando algo como BCEL, una clase determinada puede que ni siquiera tenga una representación en el disco.

Daniel Spiewak
fuente
Esto devuelve el ClassLoader utilizado para cargar la clase, ¿no? ¿No encuentra dónde está el archivo .class?
Koray Tugay
1
No, no lo hace. El cargador de clases en realidad puede referirse a una ruta de clase completamente diferente, lo que significa que será totalmente incapaz de leer la ubicación real de la clase.
Tomáš Zato - Restablece a Mónica