Trova da dove viene caricata la class java

Qualcuno sa come scoprire in modo programmatico da dove il classloader java carica effettivamente la class?

Lavoro spesso su progetti di grandi dimensioni in cui il classpath diventa molto lungo e la ricerca manuale non è davvero un’opzione. Recentemente ho avuto un problema in cui il classloader stava caricando una versione errata di una class perché era sul classpath in due posti diversi.

Quindi, come posso far sì che il classloader mi indichi da dove proviene il file della class?

Modifica: Che cosa succede se il classloader non riesce a caricare la class a causa di una mancata corrispondenza della versione (o qualcos’altro), è comunque ansible scoprire quale file tenta di leggere prima di leggerlo?

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 

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

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

Questo è quello che usiamo:

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

Questo funzionerà a seconda dell’implementazione di ClassLoader: getClass().getProtectionDomain().getCodeSource().getLocation()

La versione di Jon fallisce quando il ClassLoader dell’object è registrato come null che sembra implicare che sia stato caricato dal Boot ClassLoader .

Questo metodo si occupa di 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"; } 

Modifica solo la prima 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!

Dai un’occhiata a questa domanda simile. Strumento per scoprire la stessa class ..

Penso che l’ostacolo più rilevante sia che tu abbia un classloader personalizzato (che carica da un db o da un ldap)

In genere, non è cosa usare hardcoding. Possiamo prima ottenere className e quindi utilizzare ClassLoader per ottenere l’URL della class.

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

Questo approccio funziona sia per file che per file jar:

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

Supponendo che tu stia lavorando con una class chiamata MyClass , quanto segue dovrebbe funzionare:

 MyClass.class.getClassLoader(); 

Se è ansible ottenere o meno la posizione su disco del file .class dipende dal classloader stesso. Ad esempio, se stai usando qualcosa come BCEL, una certa class potrebbe non avere nemmeno una rappresentazione su disco.