Comment charger un fichier jar à l'exécution [dupliquer]

cette question a déjà une réponse ici:

on m'a demandé de construire un système java qui aura la capacité de charger du nouveau code (extensions) pendant l'exécution. Comment recharger un fichier jar pendant que mon code est en cours d'exécution? ou comment puis-je charger un nouveau pot?

évidemment, puisque le temps de disponibilité constant est important, j'aimerais ajouter la possibilité de recharger les classes existantes pendant qu'on y est (si cela ne complique pas trop les choses).

quelles sont les choses que je dois surveiller? (pensez à cela comme deux questions différentes - Une concernant le rechargement des classes à l'exécution, l'autre concernant l'ajout de nouvelles classes).

72
demandé sur Paul Richter 2008-10-12 01:42:22

5 réponses

recharger les classes existantes avec des données existantes est susceptible de casser les choses.

vous pouvez charger le nouveau code dans les chargeurs de nouvelle classe relativement facilement:

ClassLoader loader = URLClassLoader.newInstance(
    new URL[] { yourURL },
    getClass().getClassLoader()
);
Class<?> clazz = Class.forName("mypackage.MyClass", true, loader);
Class<? extends Runnable> runClass = clazz.asSubclass(Runnable.class);
// Avoid Class.newInstance, for it is evil.
Constructor<? extends Runnable> ctor = runClass.getConstructor();
Runnable doRun = ctor.newInstance();
doRun.run();
Les chargeurs de la classe

qui ne sont plus utilisés peuvent être ramassés (à moins qu'il n'y ait une fuite de mémoire, comme c'est souvent le cas avec les pilotes ThreadLocal, JDBC, java.beans , etc.).

si vous voulez garder les données de l'objet, alors je suggère un mécanisme de persistance tel que: La sérialisation, ou ce que vous êtes habitué.

bien sûr, les systèmes de débogage peuvent faire des choses plus fantaisistes, mais sont plus hacky et moins fiables.

Il est possible d'ajouter de nouvelles classes dans un chargeur de classe. Par exemple, en utilisant URLClassLoader.addURL . Cependant, si une classe ne se charge pas (parce que, disons, vous ne l'avez pas ajoutée), alors elle ne se chargera jamais dans cette instance de chargement de classe.

79
répondu Tom Hawtin - tackline 2008-10-12 10:55:23

ça marche pour moi:

File file  = new File("c:\myjar.jar");

URL url = file.toURL();  
URL[] urls = new URL[]{url};

ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass("com.mypackage.myclass");
34
répondu Yannick Rot 2015-12-07 19:48:44

on m'a demandé de construire un système java qui aura la capacité de charger du nouveau code pendant l'exécution""

vous pourriez vouloir baser votre système sur OSGi (ou au moins prendre beaucoup à elle), qui a été faite pour exactement cette situation.

jouer avec classloaders est vraiment une affaire délicate, principalement en raison de la façon dont la visibilité de classe fonctionne, et vous ne voulez pas courir dans des problèmes difficiles à déboguer plus tard. Par exemple, la classe .forName () , qui est largement utilisé dans de nombreuses bibliothèques ne fonctionne pas très bien sur un espace classloader fragmenté.

8
répondu Thilo 2014-03-12 22:29:07

j'ai googlé un peu, et j'ai trouvé ce code ici :

File file = getJarFileToLoadFrom();   
String lcStr = getNameOfClassToLoad();   
URL jarfile = new URL("jar", "","file:" + file.getAbsolutePath()+"!/");    
URLClassLoader cl = URLClassLoader.newInstance(new URL[] {jarfile });   
Class loadedClass = cl.loadClass(lcStr);   

Peut-on partager des avis/commentaires/réponses au sujet de cette approche?

3
répondu Amir Arad 2014-08-04 13:03:49

Utiliser org.openide.util.Lookup et ClassLoader pour charger dynamiquement le plugin Jar, comme montré ici.

public LoadEngine() {
    Lookup ocrengineLookup;
    Collection<OCREngine> ocrengines;
    Template ocrengineTemplate;
    Result ocrengineResults;
    try {
        //ocrengineLookup = Lookup.getDefault(); this only load OCREngine in classpath of  application
        ocrengineLookup = Lookups.metaInfServices(getClassLoaderForExtraModule());//this load the OCREngine in the extra module as well
        ocrengineTemplate = new Template(OCREngine.class);
        ocrengineResults = ocrengineLookup.lookup(ocrengineTemplate); 
        ocrengines = ocrengineResults.allInstances();//all OCREngines must implement the defined interface in OCREngine. Reference to guideline of implement org.openide.util.Lookup for more information

    } catch (Exception ex) {
    }
}

public ClassLoader getClassLoaderForExtraModule() throws IOException {

    List<URL> urls = new ArrayList<URL>(5);
    //foreach( filepath: external file *.JAR) with each external file *.JAR, do as follows
    File jar = new File(filepath);
    JarFile jf = new JarFile(jar);
    urls.add(jar.toURI().toURL());
    Manifest mf = jf.getManifest(); // If the jar has a class-path in it's manifest add it's entries
    if (mf
            != null) {
        String cp =
                mf.getMainAttributes().getValue("class-path");
        if (cp
                != null) {
            for (String cpe : cp.split("\s+")) {
                File lib =
                        new File(jar.getParentFile(), cpe);
                urls.add(lib.toURI().toURL());
            }
        }
    }
    ClassLoader cl = ClassLoader.getSystemClassLoader();
    if (urls.size() > 0) {
        cl = new URLClassLoader(urls.toArray(new URL[urls.size()]), ClassLoader.getSystemClassLoader());
    }
    return cl;
}
2
répondu Doan Huynh 2014-07-04 22:13:16