Encontre onde a class java é carregada

Alguém sabe como programaticamente descobrir onde o classloader java realmente carrega a class?

Costumo trabalhar em grandes projetos onde o classpath fica muito longo e a busca manual não é realmente uma opção. Recentemente, tive um problema em que o carregador de classs estava carregando uma versão incorreta de uma class porque estava no caminho de class em dois locais diferentes.

Então, como posso obter o classloader para me dizer onde no disco o arquivo de class real está vindo?

Edit: E se o classloader realmente falhar ao carregar a class devido a uma incompatibilidade de versão (ou alguma outra coisa), existe alguma maneira que possamos descobrir qual arquivo está tentando ler antes de lê-lo?

Aqui está um exemplo:

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

Isto impresso:

 file:/C:/Users/Jon/Test/foo/Test.class 

Outra maneira de descobrir de onde uma class é carregada (sem manipular a origem) é iniciar a VM Java com a opção: -verbose:class

 getClass().getProtectionDomain().getCodeSource().getLocation(); 

Isto é o que nós usamos:

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

Isso funcionará dependendo da implementação do ClassLoader: getClass().getProtectionDomain().getCodeSource().getLocation()

A versão de Jon falha quando o ClassLoader do object é registrado como null que parece indicar que foi carregado pelo Boot ClassLoader .

Este método lida com esse 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"; } 

Editar apenas a primeira linha: Main .class

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

Saída:

 /C:/Users/Test/bin/ 

Talvez o estilo ruim, mas funciona bem!

Dê uma olhada nessa questão semelhante. Ferramenta para descobrir a mesma class ..

Eu acho que o obstáculo mais relevante é se você tem um classloader personalizado (carregando de um database ou ldap)

Normalmente, não usamos o que usar a codificação. Podemos obter className primeiro e, em seguida, usar ClassLoader para obter o URL da class.

  String className = MyClass.class.getName().replace(".", "/")+".class"; URL classUrl = MyClass.class.getClassLoader().getResource(className); String fullPath = classUrl==null ? null : classUrl.getPath(); 

Essa abordagem funciona para arquivos e jars:

 Class clazz = Class.forName(nameOfClassYouWant); URL resourceUrl = clazz.getResource("/" + clazz.getCanonicalName().replace(".", "/") + ".class"); InputStream classStream = resourceUrl.openStream(); // load the bytecode, if you wish 

Supondo que você esteja trabalhando com uma class chamada MyClass , o seguinte deve funcionar:

 MyClass.class.getClassLoader(); 

Se você pode ou não obter a localização no disco do arquivo .class depende do próprio carregador de class. Por exemplo, se você estiver usando algo como o BCEL, uma determinada class pode nem ter uma representação em disco.