Comment mettre ClassLoader personnalisé à utiliser?

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 créées dynamiquement pour être utilisées par une bibliothèque tierce via un ClassLoader personnalisé.

Maintenant, mon problème est: Comment puis-je définir mon ClassLoader personnalisé pour être utilisé pour charger ces classes quand je ne les charge pas directement moi-même?

Je pensais que quand j'ai utilisé mon ClassLoader pour charger une certaine classe, il est devenu le ClassLoader de cette classe, et tout 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 l'utiliser 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 3ème partie, le ClassLoader parent trouve cette classe (car il est sur le classpath) et devient son ClassLoader. Et puisque le ClassLoader parent ne connaît pas mon ClassLoader personnalisé, il est effectivement hors d'usage et this.getClass().getClassLoader() ne peut pas être converti en DynamicClassLoader.

Une autre approche serait de définir mon ClassLoader comme 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, mais je suis maintenant de bonnes idées... toute aide est grandement appréciée!

27
demandé sur mkobit 2010-11-04 14:02:48

1 réponses

Je ne suis pas sûr de comprendre la question, vous avez une 3rd party lib et vous voulez qu'elle utilise votre classloader pour charger des classes.

Si vous avez de la chance, la lib tierce utilise le classloader de contexte threads que vous pouvez définir en utilisant Thread.currentThread().setContextClassLoader(myClassLoader), dans le même thread, vous pouvez accéder à ce classloader avec Thread.currentThread().getContextClassLoader()...

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

Édité après votre commentaire:

Parent_last classloader fera une différence si votre bibliothèque ne repose pas sur le classloader de contexte de thread, alors vous devez charger la bibliothèque avec votre ClassLoader parent-last définissant ainsi votre classloader comme classloader pour la bibliothèque au lieu de son chargeur parent (le parent de votre classloader)...

Vous pouvez également créer un classloader avec un comportement parent-premier mais pour votre 3ème partie de la bibliothèque...

Et un bon lien sur classloaders...

21
répondu pgras 2015-11-30 02:05:21