Cela ne répond pas à la question d'origine, mais comme la question est hautement classée et liée à toute ContextClassLoader
requête, je pense qu'il est important de répondre à la question connexe de savoir quand le chargeur de classe de contexte doit être utilisé. Réponse courte: n'utilisez jamais le chargeur de classe de contexte ! Mais définissez-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 de charger une autre classe, le chargeur de classe correct à utiliser est le même chargeur de classe que la classe appelante (c.-à getClass().getClassLoader()
-d.). C'est ainsi que les choses fonctionnent 99,9% du temps car c'est ce que la JVM fait elle-même la première fois que vous construisez une instance d'une nouvelle classe, invoquez une méthode statique ou accédez à un champ statique.
Lorsque vous souhaitez créer une classe à l'aide de la réflexion (comme 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 quel chargeur de classe utiliser, en recevant le ClassLoader
comme paramètre de l'application. L'application (qui connaît toutes les classes qui ont besoin d'être construites) doit la passer getClass().getClassLoader()
.
Toute autre façon d'obtenir un chargeur de classe est incorrecte. Si une bibliothèque utilise des hacks tels que Thread.getContextClassLoader()
, sun.misc.VM.latestUserDefinedLoader()
ou sun.reflect.Reflection.getCallerClass()
s'il s'agit d'un bogue provoqué par une déficience de l'API. Fondamentalement, cela Thread.getContextClassLoader()
n'existe que parce que celui qui a conçu l' ObjectInputStream
API a oublié d'accepter le ClassLoader
paramètre, et cette erreur a hanté la communauté Java à ce jour.
Cela dit, de nombreuses classes JDK utilisent l'un des quelques hacks pour deviner quel chargeur de classe utiliser. Certains utilisent le ContextClassLoader
(qui échoue lorsque vous exécutez différentes applications sur un pool de threads partagé ou lorsque vous quittez le ContextClassLoader null
), certains parcourent la pile (qui échoue lorsque l'appelant direct de la classe est lui-même une bibliothèque), certains utilisent le chargeur de classe système (ce qui est bien, tant qu'il est documenté d'utiliser uniquement des classes dans le 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). Cela a entraîné beaucoup de pleurs et de grincements de dents.
Lorsque vous utilisez une telle API, essayez d'abord 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 ContextClassLoader
avant l'appel de l'API (et de 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);
}
ClassB
doit être sur le chemin de classe duClassA
chargeur (ouClassA
des parents du chargeur)? N'est-il pas possible pourClassA
le chargeur de surchargerloadClass()
, de sorte qu'il puisse se charger avec succèsClassB
même s'ilClassB
n'est pas sur son chemin de classe?