2008-10-11 9 views
71

On m'a demandé de construire un système Java qui aura la capacité de charger du nouveau code (expansions) en cours d'exécution. Comment recharger un fichier jar pendant que mon code est en cours d'exécution? ou comment charger un nouveau pot? De toute évidence, étant donné que le temps de disponibilité constant est important, j'aimerais ajouter la possibilité de recharger les classes existantes (si cela ne complique pas trop les choses).Comment charger un fichier JAR à l'exécution

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

+0

En ce qui concerne le rechargement des classes existantes, examiner cette question connexe: - http://stackoverflow.com/questions/3216780/problem-reloading-a-jar-using-urlclassloader - http://blogs.oracle .com/CoreJavaTechTips/entry/closing_a_urlclassloader –

Répondre

79

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

Vous pouvez charger un 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(); 

chargeurs de classe ne sont plus utilisés peuvent être déchets collectés (à moins d'une fuite de mémoire, comme cela est souvent le cas avec l'utilisation ThreadLocal, JDBC pilotes, java.beans, etc.).

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

Bien sûr, les systèmes de débogage peuvent faire des choses plus sophistiquées, 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 parvient pas à charger (parce que, par exemple, vous ne l'avez pas ajoutée), elle ne sera jamais chargée dans cette instance de chargeur de classe.

+0

si mon chargeur de classe initial chargé des trucs comme threadpools et d'autres ressources, ne signifie que les nouvelles classes chargées par le second classloader ne seront pas accessibles à ces ressources? –

+0

Je n'ai pas spécifié de parent pour URLClassLoader, donc il utilisera simplement le système (je corrigerai). Cela signifiera qu'il sera toujours capable d'accéder aux classes sur le classpath. Si vous voulez partager des choses, passez des références autour de l'utilisation de types définis dans les chargeurs de classe courants (peut-être le chargeur de classe de démarrage pour Java lib). –

+2

Il me manque quelque chose: pourquoi Class.newInstance() "evil"? –

3

Je googlé un peu et trouvé ce 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); 

Can part de votre opinion de tout le monde/commentaires/réponses concernant cette approche?

+1

L'URL du jar sert à pointer sur une ressource avec un fichier jar.Vous voulez passer le fichier JAR entier à URLClassLoader. (Malheureusement, les pots dans les pots sont cassés.) –

+1

le lien est cassé. – Koekiebox

+0

lien fixe (ou plutôt, googlé "getJarFileToLoadFrom" et trouvé une autre copie du code qui précède ma question: une approche "éventuellement cohérente" à "Internet") –

8

On m'a demandé de construire un système java qui aura la possibilité de charger un nouveau code lors de l'exécution

Vous pouvez baser votre système sur OSGi (ou au moins prendre beaucoup à elle) , qui a été fait pour exactement cette situation.

Messing avec classloaders est très compliqué, principalement en raison du fonctionnement de la visibilité de classe, et vous ne voulez pas vous lancer dans des problèmes difficiles à déboguer plus tard. Par exemple, Class.forName(), qui est largement utilisé dans de nombreuses bibliothèques, ne fonctionne pas très bien sur un espace de classeloader fragmenté.

+0

avec OSGi, vous pouvez 1. remplacer les jars en cours d'exécution. 2. Gardez plusieurs versions du même pot lorsque différentes parties de votre application dépendent de différentes versions. 3. d'autres fonctionnalités comme la gestion de la configuration, les usines de services et bien d'autres. – Gadi

33

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

cela semble fonctionner :) – zproxy

+4

Cela ne fonctionne pas pour moi .. – Gynnad

+10

Selon [Oracle] (http://docs.oracle.com/javase/7/docs/api/java/io/File.html) à la place d'utiliser '.toURL()' utilise '.toURI(). toURL()'. –

2

Utilisez org.openide.util.Lookup et ClassLoader pour charger dynamiquement le plugin Jar, comme indiqué 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; 
} 
+0

Vous aimez ça compliqué. – Elmue

Questions connexes