2008-10-11 12 views
71

Ich wurde gebeten, ein Java-System zu erstellen, das während des Betriebs neuen Code (Erweiterungen) laden kann. Wie kann ich eine JAR-Datei erneut laden, während mein Code ausgeführt wird? oder wie lade ich ein neues Glas?So laden Sie eine JAR-Datei zur Laufzeit

Offensichtlich, da konstante Betriebszeit wichtig ist, möchte ich die Fähigkeit hinzufügen, existierende Klassen währenddessen neu zu laden (wenn es Sachen nicht zu viel kompliziert).

Auf welche Dinge sollte ich achten? (Betrachten Sie es als zwei verschiedene Fragen - eine über das erneute Laden von Klassen zur Laufzeit, die andere über das Hinzufügen neuer Klassen).

+0

In Hinblick auf bestehende Klassen neu zu laden, sollten Sie diese verwandtes Thema: - http://stackoverflow.com/questions/3216780/problem-reloading-a-jar-using-urlclassloader - http://blogs.oracle .com/CoreJavaTechTips/entry/closing_a_urlclassloader –

Antwort

79

Das Zurückladen vorhandener Klassen mit vorhandenen Daten führt wahrscheinlich zu einer Störung.

Sie können relativ leicht neuen Code in neue Klasse Lader laden:

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

Klasse Lader nicht mehr verwendet wird Müll gesammelt werden kann (es sei denn, ein Speicherverlust ist, wie es oft der Fall ist Thread mit Verwendung von JDBC Treiber, java.beans, usw.).

Wenn Sie die Objektdaten beibehalten möchten, empfehle ich einen Persistenzmechanismus wie Serialisierung oder was auch immer Sie gewohnt sind.

Natürlich können Debugging-Systeme schickere Dinge tun, sind aber hacky und weniger zuverlässig.

Es ist möglich, neue Klassen einem Klassenlader hinzuzufügen. Zum Beispiel mit URLClassLoader.addURL. Wenn eine Klasse jedoch nicht geladen werden kann (weil Sie sie beispielsweise nicht hinzugefügt haben), wird sie nie in dieser Klassenladerinstanz geladen.

+0

Wenn meine ursprüngliche Classloader Sachen wie Threadpools und andere Ressourcen geladen, tut es nicht bedeuten, dass die neuen Klassen, die vom zweiten Classloader geladen werden, für diese Ressourcen nicht zugänglich sind? –

+0

Ich habe kein Elternteil für den URLClassLoader angegeben, also wird es nur das System verwenden (ich werde es beheben). Das bedeutet, dass es weiterhin auf Klassen im Klassenpfad zugreifen kann. Wenn Sie Inhalte freigeben möchten, übergeben Sie Referenzen um Typen, die in allgemeinen Klassenladern definiert sind (möglicherweise der Boot-Klassenlader für Java-Bibliotheken). –

+2

Ich vermisse etwas: Warum ist Class.newInstance() "böse"? –

3

gegoogelt ich ein wenig und fand diesen Code here:

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

Kann jeder Aktie Meinungen/Kommentare/Antworten zu diesem Ansatz?

+1

Die jar-URL zeigt auf eine Ressource mit einer jar-Datei.Sie möchten die gesamte JAR-Datei an URLClassLoader übergeben. (Leider Gläser innerhalb Gläser ist gebrochen.) –

+1

der Link ist kaputt. – Koekiebox

+0

Fixed Link (oder besser, gegoogelt "getJarFileToLoadFrom" und fand eine andere Kopie des Codes, der vor meiner Frage: eine "schließlich konsistente" Ansatz zu "Internet") –

8

ich gebeten wurde, ein Java-System aufzubauen, das die Fähigkeit, neue Code haben zu laden, während

läuft Vielleicht möchten Sie Ihr System auf OSGi (oder zumindest zu nehmen viel auf sie) stützen , die genau für diese Situation gemacht wurde.

Mit Classloadern zu arbeiten ist wirklich kniffeliges Geschäft, vor allem wegen der Klassensichtbarkeit, und Sie wollen später nicht auf schwer zu debuggende Probleme stoßen. Zum Beispiel funktioniert Class.forName(), das in vielen Bibliotheken weit verbreitet ist, nicht gut auf einem fragmentierten Klassenladerplatz.

+0

mit OSGi können Sie 1. Jars in Runtime ersetzen. 2. behalte mehrere Versionen desselben Krugs, wenn verschiedene Teile deiner App von verschiedenen Versionen abhängen. 3. Weitere Funktionen wie Konfigurationsmanagement, Service Factories und vieles mehr. – Gadi

33

Dies funktioniert für mich:

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

das scheint zu funktionieren :) – zproxy

+4

Dies funktioniert nicht für mich .. – Gynnad

+10

Nach [Oracle] (http://docs.oracle.com/javase/7/docs/api/java/io/File.html) statt Verwenden von '.toURL()' verwenden '.toURI(). toURL()'. –

2

Verwenden org.openide.util.Lookup und Classloader, um dynamisch die Jar-Plugin zu laden, wie hier gezeigt.

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; 
} 
+0

Sie mögen es kompliziert. – Elmue