web-dev-qa-db-fra.com

Comment utiliser ClassLoader personnalisé?

Bonjour à tous et merci pour l'attention! J'ai un problème qui doit être à la fois facile et évident, mais je suis coincé.

Je veux fournir des classes Java Java créées dynamiquement à utiliser par une bibliothèque tierce via un ClassLoader personnalisé.

Maintenant, mon problème est le suivant: comment définir mon ClassLoader personnalisé pour qu'il soit utilisé pour charger ces classes lorsque je ne les charge pas directement moi-même?

J'ai pensé que lorsque j'utilisais mon ClassLoader pour charger une certaine classe, il devenait le ClassLoader de cette classe, et toutes les classes chargées à partir de cette classe seraient canalisées via mon ClassLoader.

J'ai créé un ClassLoader personnalisé, en suivant ce tutoriel officiel: http://Java.Sun.com/developer/onlineTraining/Security/Fundamentals/magercises/ClassLoader/help.html .

public class DynamicClassloader extends ClassLoader {

    private Map<String, Class<?>> classesMap = new HashMap<String, Class<?>>();

    public DynamicClassloader(ClassLoader parent) {
        // Also tried super(parent);
        super(Sun.misc.Launcher.getLauncher().getClassLoader());
    }

    // Adding dynamically created classes
    public void defineClass(String name, Class<?> clazz) {
        classesMap.put(name, clazz);
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // load from parent
        Class<?> result = findLoadedClass(name);
        if (result != null) {
            return result;
        }
        try {
            result = findSystemClass(name);
        } catch (Exception e) {
            // Ignore these
        }
        if (result != null) {
            return result;
        }
        result = classesMap.get(name);
        if (result == null) {
            throw new ClassNotFoundException(name);
        }
        return result;
    }
}

Je voulais utiliser cela ailleurs dans le code comme ça:

ClassLoader thisClassLoader = this.getClass().getClassLoader();
((DynamicClassloader) thisClassLoader).defineClass(className, dynClass);

Maintenant, mon problème est que lorsque j'appelle findSystemClass(name) de la classe de bibliothèque tierce, le ClassLoader parent trouve cette classe (car elle se trouve sur le chemin de classe) et devient son ClassLoader. Et puisque le ClassLoader parent ne connaît pas mon ClassLoader personnalisé, il est effectivement mis hors service et this.getClass().getClassLoader() ne peut pas être casté en DynamicClassLoader.

Une autre approche serait de définir mon ClassLoader comme étant le ClassLoader système via l'argument JVM -Djava.system.class.loader=my.DynamicClassloader. Mais cela me donne une StackOverflowError:

    ...
at de.unisaarland.cs.st.DynamicClassloader.findClass(DynamicClassloader.Java:39)
at Java.lang.ClassLoader.loadClass(ClassLoader.Java:307)
at Java.lang.ClassLoader.loadClass(ClassLoader.Java:248)
at Java.lang.ClassLoader.findSystemClass(ClassLoader.Java:916)
at de.unisaarland.cs.st.DynamicClassloader.findClass(DynamicClassloader.Java:39)
    ...

Cela doit être vraiment facile à faire, pourtant je suis à court d'idées ... toute aide est grandement appréciée!

30
roesslerj

Je ne suis pas sûr de comprendre la question, vous avez une bibliothèque tierce et vous souhaitez qu'elle utilise votre chargeur de classe pour charger les classes.

Si vous êtes chanceux, la bibliothèque tierce utilise le chargeur de classe de contexte de threads que vous pouvez définir à l'aide de Thread.currentThread().setContextClassLoader(myClassLoader), dans le même thread, vous pouvez accéder à ce chargeur de classe avec Thread.currentThread().getContextClassLoader()...

Un autre point, mais pas sûr qu'il soit important dans votre contexte, est que vous pouvez également écrire un chargeur de classe parent-dernier qui tentera de charger la classe avant de déléguer à son parent (au lieu d'essayer de déléguer en premier)

Modifié après votre commentaire:

parent_last classloader fera une différence si votre bibliothèque ne dépend pas du chargeur de classe de contexte de thread, vous devez alors charger la bibliothèque avec votre parent-dernier classloader définissant ainsi votre classloader comme classloader pour la bibliothèque au lieu de son parent loader (le parent de votre chargeur de classe) ...

Vous pouvez également créer un chargeur de classe avec un comportement d'abord parent mais pour votre bibliothèque tierce ...

Et un bon lien sur les chargeurs de classe ...

22
pgras