Cela ne répond pas à la question initiale, mais étant donné que la question est très bien classée et liée à n'importe quelle autre question, il est possible d'y répondre. ContextClassLoader
Je pense qu'il est important de répondre à la question connexe de savoir quand le chargeur de classes contextuelles doit être utilisé. Réponse courte : ne jamais utiliser le chargeur de classe contextuel ! Mais réglez-le sur getClass().getClassLoader()
lorsque vous devez appeler une méthode à laquelle il manque un ClassLoader
paramètre.
Lorsque le code d'une classe demande à charger une autre classe, le chargeur de classe correct à utiliser est le même chargeur de classe que la classe de l'appelant (c'est-à-dire, getClass().getClassLoader()
). C'est ainsi que les choses fonctionnent dans 99,9 % des cas, car c'est ce que fait la JVM elle-même la première fois que vous construisez une instance d'une nouvelle classe, que vous invoquez une méthode statique ou que vous accédez à un champ statique.
Lorsque vous souhaitez créer une classe en utilisant la réflexion (par exemple lors de la désérialisation ou du chargement d'une classe nommée configurable), la bibliothèque qui effectue la réflexion doit toujours demander l'application le chargeur de classe à utiliser, en recevant le fichier ClassLoader
en tant que paramètre de l'application. L'application (qui connaît toutes les classes qui doivent être construites) doit lui passer getClass().getClassLoader()
.
Toute autre façon d'obtenir un chargeur de classe est incorrecte. Si une bibliothèque utilise des astuces telles que Thread.getContextClassLoader()
, sun.misc.VM.latestUserDefinedLoader()
ou sun.reflect.Reflection.getCallerClass()
il s'agit d'un bogue causé par une déficience de l'API. En fait, il s'agit d'un bogue causé par une déficience de l'API, Thread.getContextClassLoader()
n'existe que parce que celui qui a conçu le ObjectInputStream
L'API a oublié d'accepter le ClassLoader
en tant que paramètre, et cette erreur a hanté la communauté Java jusqu'à ce jour.
Cela dit, de nombreuses classes du JDK utilisent l'une de ces quelques astuces pour deviner le chargeur de classe à utiliser. Certaines utilisent la méthode ContextClassLoader
(qui échoue lorsque vous exécutez différentes applications sur un pool de threads partagés, ou lorsque vous laissez la fonction ContextClassLoader null
), d'autres utilisent la pile (ce qui échoue lorsque l'appelant direct de la classe est lui-même une bibliothèque), d'autres encore utilisent le chargeur de classe du système (ce qui est très bien, tant qu'il est documenté pour n'utiliser que des classes dans le répertoire CLASSPATH
) ou le chargeur de classe bootstrap, et certains utilisent une combinaison imprévisible des techniques ci-dessus (ce qui ne fait que rendre les choses plus confuses). Il en résulte beaucoup de pleurs et de grincements de dents.
Lors de l'utilisation d'une telle API, d'abord, essayer de trouver une surcharge de la méthode qui accepte le chargeur de classe comme paramètre . S'il n'y a pas de méthode sensée, essayez de définir le paramètre ContextClassLoader
avant l'appel à l'API (et le réinitialiser après) :
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);
}