web-dev-qa-db-fra.com

Comment charger un fichier jar au moment de l'exécution

On m'a demandé de créer un système Java) capable de charger du nouveau code (extensions) tout en cours d'exécution. Comment recharger un fichier jar alors que mon code est en cours d'exécution? Je charge un nouveau pot?

De toute évidence, étant donné que le temps d’activation est important, j’aimerais ajouter la possibilité de recharger des classes existantes en même temps (si cela ne complique pas trop les choses).

Quelles sont les choses que je devrais rechercher? (Pensez-y comme à deux questions différentes: l’une concernant le rechargement de classes au moment de l’exécution, l’autre concernant l’ajout de nouvelles classes).

72
Amir Arad

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

Vous pouvez charger le nouveau code dans de nouveaux chargeurs de classes 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 classe qui ne sont plus utilisés peuvent être nettoyés (à moins d’une fuite de mémoire, comme c’est souvent le cas avec ThreadLocal, les pilotes JDBC, Java.beans, etc).

Si vous souhaitez conserver les données d'objet, je suggère un mécanisme de persistance tel que la sérialisation, ou tout ce que vous avez l'habitude de faire.

Bien sûr, les systèmes de débogage peuvent faire des choses plus sophistiquées, mais sont plus robustes et moins fiables.

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

79

Cela fonctionne 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");
35
punkers

On m'a demandé de construire un système Java qui permettra de charger du nouveau code pendant l'exécution

Vous voudrez peut-être baser votre système sur OSGi (ou du moins à en prendre beaucoup), ce qui a été conçu exactement pour cette situation.

S'amuser avec les chargeurs de classes est une tâche très délicate, principalement à cause de la visibilité de la classe, et vous ne voulez pas rencontrer de problèmes difficiles à résoudre ultérieurement. Par exemple, Class.forName () , qui est largement utilisé dans de nombreuses bibliothèques, ne fonctionne pas très bien sur un espace de chargeur de classe fragmenté.

8
Thilo

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);   

Quelqu'un peut-il partager des opinions/commentaires/réponses concernant cette approche?

3
Amir Arad

Utilisez org.openide.util.Lookup et ClassLoader pour charger dynamiquement le plug-in Jar, comme illustré 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
Doan Huynh