Questo non risponde alla domanda originale, ma poiché la domanda è classificata e collegata per qualsiasi ContextClassLoader
query, penso che sia importante rispondere alla domanda correlata su quando utilizzare il caricatore di classi di contesto. Risposta breve: non usare mai il caricatore di classi di contesto ! Ma impostalo su getClass().getClassLoader()
quando devi chiamare un metodo in cui manca un ClassLoader
parametro.
Quando il codice di una classe chiede di caricare un'altra classe, il caricatore di classi corretto da utilizzare è lo stesso caricatore di classi della classe chiamante (ovvero, getClass().getClassLoader()
). Questo è il modo in cui le cose funzionano il 99,9% delle volte perché è ciò che la JVM fa da sola la prima volta che costruisci un'istanza di una nuova classe, invoca un metodo statico o accedi a un campo statico.
Quando si desidera creare una classe utilizzando la riflessione (come quando si deserializza o si carica una classe denominata configurabile), la libreria che esegue la riflessione dovrebbe sempre chiedere all'applicazione quale caricatore di classe utilizzare, ricevendo ClassLoader
come parametro dall'applicazione. L'applicazione (che conosce tutte le classi che devono essere costruite) dovrebbe passarla getClass().getClassLoader()
.
Qualsiasi altro modo per ottenere un caricatore di classi non è corretto. Se una libreria utilizza hack come Thread.getContextClassLoader()
, sun.misc.VM.latestUserDefinedLoader()
o sun.reflect.Reflection.getCallerClass()
è un bug causato da un difetto nell'API. Fondamentalmente, Thread.getContextClassLoader()
esiste solo perché chiunque abbia progettato l' ObjectInputStream
API ha dimenticato di accettarlo ClassLoader
come parametro e questo errore ha perseguitato la comunità Java fino ad oggi.
Detto questo, molte molte classi JDK usano uno dei pochi hack per indovinare un caricatore di classi da usare. Alcuni usano il ContextClassLoader
(che fallisce quando si eseguono app diverse su un pool di thread condiviso o quando si lascia il ContextClassLoader null
), altri camminano nello stack (che fallisce quando il chiamante diretto della classe è esso stesso una libreria), altri usano il caricatore di classi di sistema (il che va bene, purché sia documentato l'uso solo delle classi nel CLASSPATH
) o caricatore di classi bootstrap, e alcuni usano una combinazione imprevedibile delle tecniche di cui sopra (il che rende le cose più confuse). Ciò ha provocato molto pianto e digrignamento dei denti.
Quando si utilizza un'API di questo tipo, provare innanzitutto a trovare un sovraccarico del metodo che accetta il programma di caricamento classi come parametro . Se non esiste un metodo ragionevole, prova a impostare ContextClassLoader
prima della chiamata API (e reimpostarla in seguito):
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
// call some API that uses reflection without taking ClassLoader param
} finally {
Thread.currentThread().setContextClassLoader(originalClassLoader);
}
ClassB
deve essere sul percorso di classe delClassA
caricatore (oClassA
dei genitori del caricatore)? Non è possibileClassA
eseguire l'override del caricatoreloadClass()
, in modo che possa essere caricato correttamenteClassB
anche quandoClassB
non si trova sul suo percorso di classe?