Domanda

    

Questa domanda ha già una risposta qui:

         

Mi è stato chiesto di costruire un sistema java che avrà la possibilità di caricare nuovo codice (espansioni) durante l'esecuzione. Come ricaricare un file jar mentre il mio codice è in esecuzione? o come posso caricare un nuovo vaso?

Ovviamente, dato che il tempo di attività costante è importante, mi piacerebbe aggiungere la possibilità di ricaricare le classi esistenti mentre ci si trova (se non complica troppo le cose).

Quali sono le cose che dovrei cercare? (pensala come due domande diverse: una riguardante il ricaricamento delle classi in fase di esecuzione, l'altra riguardante l'aggiunta di nuove classi).

È stato utile?

Soluzione

È probabile che il ricaricamento delle classi esistenti con dati esistenti rompa qualcosa.

Puoi caricare il nuovo codice nei caricatori di nuove classi in modo relativamente semplice:

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

I caricatori di classi non più utilizzati possono essere sottoposti a garbage collection (a meno che non vi sia una perdita di memoria, come spesso accade con l'utilizzo di ThreadLocal, driver JDBC, java.beans , ecc.)

Se vuoi conservare i dati dell'oggetto, ti suggerisco un meccanismo di persistenza come la serializzazione o qualunque cosa tu sia abituato.

Ovviamente i sistemi di debug possono fare cose più fantasiose, ma sono più confuse e meno affidabili.

È possibile aggiungere nuove classi in un caricatore di classi. Ad esempio, utilizzando URLClassLoader.addURL . Tuttavia, se una classe non riesce a caricarsi (perché, per esempio, non l'hai aggiunta), non verrà mai caricata nell'istanza del caricatore di classe.

Altri suggerimenti

Questo funziona per me:

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

Mi è stato chiesto di creare un sistema java che avrà la possibilità di caricare nuovo codice durante l'esecuzione

Potresti voler basare il tuo sistema su OSGi (o almeno prendere molto su esso), che è stato creato proprio per questa situazione.

Fare casini con i caricatori di classi è un affare davvero complicato, principalmente a causa del modo in cui funziona la visibilità delle classi, e in seguito non vorrai incontrare problemi difficili da eseguire il debug. Ad esempio, Class.forName () , che è ampiamente utilizzato in molte librerie non funziona troppo bene su uno spazio frammentato del classloader.

Ho cercato su Google un po 'e ho trovato questo codice qui :

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

Qualcuno può condividere opinioni / commenti / risposte su questo approccio?

Utilizza org.openide.util.Lookup e ClassLoader per caricare dinamicamente il plug-in Jar, come mostrato qui.

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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top