Encontre de onde a classe java é carregada

184

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

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

Então, como posso fazer com que o carregador de classes me diga de onde vem o arquivo de classe real?

Edit: E se o carregador de classes realmente não carregar a classe devido a uma incompatibilidade de versão (ou outra coisa), existe alguma maneira de descobrir qual arquivo está tentando ler antes de lê-lo?

Lucas
fonte
4
@JarrodRoberson Eu não acho que isso deve ser considerado uma duplicata de stackoverflow.com/questions/11747833/... uma vez que esta pergunta foi feita em 2008 e que pergunta foi feita em 2012
luke

Respostas:

189

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"));
    }
}

Este impresso:

file:/C:/Users/Jon/Test/foo/Test.class
Jon Skeet
fonte
32
Para reduzir a digitação redundante, também é possível usar a versão mais curta:, Test.class.getResource("Test.class")que não repete o nome do pacote.
meriton
1
E se a classe for compilada, por exemplo, a partir de um arquivo .groovy?
Ondra Žižka
35
@meriton: Ou, para sobreviver refactorinsgs:Test.class.getResource(Test.class.getSimpleName() + ".class")
leonbloy
1
Para BouncyCastleProvidero nome completo do pacote, no entanto, é necessário.
Pavel Vlasov #
3
É possível getClassLoader()retornar null. Veja aqui uma extensão deste método para lidar com isso.
OldCurmudgeon
100

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

jiriki
fonte
6
isso funcionou muito bem, e não tem o problema de lidar com as classes com nulo ClassLoader
lexicalscope
2
@ries Se não é necessário fazer isso programaticamente, esse é definitivamente o caminho a percorrer, e resolveu o meu problema. No entanto, o OP perguntou especificamente como fazer isso programaticamente.
SantiBailors 17/02
80
getClass().getProtectionDomain().getCodeSource().getLocation();
Dave DiFranco
fonte
4
Sim, embora não funcione com um gerenciador de segurança instalado e sem as permissões necessárias.
Tom Hawtin - tackline
1
FYI, NPE = Exceção de ponteiro nulo. HTH!
precisa
Esse método é preferido desde que você tenha uma referência a uma instância, pois você pode carregar a mesma classe de dois locais diferentes.
Miguel Ping
1
Também não funciona quando chamado de um módulo Java 9+ (o que, é claro, você não poderia saber em 2008).
Jeff G
28

Isto é o que 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()

Jevgeni Kabanov
fonte
17

A versão de Jon falha quando o objeto ClassLoaderé registrado, o nullque parece implicar que ele foi carregado pela inicialização 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";
}
OldCurmudgeon
fonte
5

Edite apenas a 1ª linha: Main.class

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

System.out.println(path);

Resultado:

/C:/Users/Test/bin/

Talvez estilo ruim, mas funciona bem!

ecerer
fonte
3

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

        String className = MyClass.class.getName().replace(".", "/")+".class";
        URL classUrl  = MyClass.class.getClassLoader().getResource(className);
        String fullPath = classUrl==null ? null : classUrl.getPath();
Hongyang
fonte
Precisa ser: URL classUrl = MyClass.class.getClassLoader (). GetResource ("/" + className);
Andrew Coates
MyClass.class é parte importante - getClass () pode retornar Proxy! Em seguida, você pode obter um nome como MyClass $$ EnhancerBySpringCGLIB $$ a98db882.class e URL nulo.
jalmasi
1

Dê uma olhada nesta pergunta semelhante. Ferramenta para descobrir a mesma classe.

Eu acho que o obstáculo mais relevante é se você tiver um carregador de classe personalizado (carregando de um db ou ldap)

OscarRyz
fonte
1

Maneira simples:

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

Exemplo de saída:

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

Ou

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

Exemplo de saída:

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

seduardo
fonte
0

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
Adão
fonte
-1

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

MyClass.class.getClassLoader();

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

Daniel Spiewak
fonte
Isso retorna o ClassLoader usado para carregar a classe, não é? Não encontra onde está o arquivo .class?
Koray Tugay
1
Não, não faz. O carregador de classes pode realmente se referir a um caminho de classe completamente diferente - significa que será totalmente incapaz de obter o local real da classe.
Tomáš Zato - Restabelece Monica