Trova da dove viene caricata la classe java


184

Qualcuno sa come scoprire a livello di programmazione da dove il classloader java carica effettivamente la classe?

Lavoro spesso su grandi progetti in cui il percorso di classe diventa molto lungo e la ricerca manuale non è davvero un'opzione. Di recente ho avuto un problema in cui il classloader stava caricando una versione errata di una classe perché si trovava sul percorso di classe in due luoghi diversi.

Quindi, come posso ottenere il classloader per dirmi da dove proviene il file di classe reale?

Modifica: Che dire se il classloader non riesce a caricare la classe a causa di una mancata corrispondenza della versione (o qualcos'altro), c'è comunque modo di scoprire quale file sta cercando di leggere prima di leggerlo?


4
@JarrodRoberson Non credo che questo dovrebbe essere considerato un duplicato di stackoverflow.com/questions/11747833/… poiché questa domanda è stata posta nel 2008 e quella domanda è stata posta nel 2012
Luca

Risposte:


189

Ecco un esempio:

package foo;

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

Questo stampato:

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

32
Per ridurre la digitazione ridondante, è possibile utilizzare anche la versione più breve:, Test.class.getResource("Test.class")che non ripete il nome del pacchetto.
Meriton

1
Cosa succede se la classe viene compilata, ad esempio da un file .groovy?
Ondra Žižka,

35
@meriton: Oppure, per sopravvivere a refactorinsgs:Test.class.getResource(Test.class.getSimpleName() + ".class")
leonbloy,

1
Per BouncyCastleProvideril nome del pacchetto completo è tuttavia richiesto.
Pavel Vlasov,

3
È possibile per il getClassLoader()ritorno null. Vedi qui per un'estensione di questo metodo per gestirlo.
OldCurmudgeon

100

Un altro modo per scoprire da dove viene caricata una classe (senza manipolare l'origine) è avviare la VM Java con l'opzione: -verbose:class


6
ha funzionato molto bene e non ha il problema di gestire le classi con ClassLoader null
lexicalscope

2
@ries Se uno non ha bisogno di farlo a livello di codice, questa è sicuramente la strada da percorrere e ha risolto il mio problema. Tuttavia, l'OP ha chiesto specificamente come farlo a livello di programmazione.
SantiBailors,

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

4
Sì, anche se non funziona con un gestore della sicurezza installato e senza le autorizzazioni necessarie.
Tom Hawtin - tackline il

1
Cordiali saluti, NPE = Eccezione puntatore nullo. HTH!
Evgeni Sergeev,

Questo metodo è preferito purché si abbia un riferimento a un'istanza, poiché è possibile caricare la stessa classe da due posizioni diverse.
Miguel Ping,

1
Inoltre, non funziona quando viene chiamato da un modulo Java 9+ (che ovviamente non avresti potuto sapere nel 2008).
Jeff G

28

Questo è ciò che usiamo:

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

Questo funzionerà in base all'implementazione ClassLoader: getClass().getProtectionDomain().getCodeSource().getLocation()


17

La versione di Jon ha esito negativo quando l'oggetto ClassLoaderè registrato in quanto nullsembra implicare che sia stato caricato dal Boot ClassLoader.

Questo metodo risolve questo 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";
}

5

Modifica solo la 1a riga: Main.class

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

System.out.println(path);

Produzione:

/C:/Users/Test/bin/

Forse cattivo stile ma funziona bene!


3

In genere, non utilizziamo l'hardcoding. È possibile ottenere prima className e quindi utilizzare ClassLoader per ottenere l'URL della classe.

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

Deve essere: URL classUrl = MyClass.class.getClassLoader (). GetResource ("/" + className);
Andrew Coates,

MyClass.class è una parte importante: getClass () può restituire il proxy! Quindi puoi ottenere un nome come MyClass $$ EnhancerBySpringCGLIB $$ a98db882.class e URL null.
jalmasi,


1

Modo semplice:

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

Out Esempio:

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

O

String obj = "test semplice"; System.out.println (obj.getClass () getResource (obj.getClass () getSimpleName () +).. "Class.");

Out Esempio:

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


0

Questo approccio funziona sia per file che per vasi:

Class clazz = Class.forName(nameOfClassYouWant);

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

-1

Supponendo che tu stia lavorando con una classe denominata MyClass, dovrebbe funzionare quanto segue:

MyClass.class.getClassLoader();

La possibilità di ottenere o meno la posizione su disco del file .class dipende dal classloader stesso. Ad esempio, se stai usando qualcosa come BCEL, una certa classe potrebbe non avere nemmeno una rappresentazione su disco.


Questo restituisce il ClassLoader utilizzato per caricare la classe, non è vero? Non trova dove si trova il file .class?
Koray Tugay,

1
No non lo fa. Il classloader può effettivamente riferirsi a un percorso di classe completamente diverso, il che significa che non sarà totalmente in grado di raggiungere la posizione effettiva della classe.
Tomáš Zato - Ripristina Monica
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.