2008-10-11 14 views
71

Me pidieron que construyera un sistema java que tenga la capacidad de cargar código nuevo (expansiones) mientras se ejecuta. ¿Cómo recargo un archivo jar mientras mi código se está ejecutando? o ¿cómo cargo un frasco nuevo?Cómo cargar un archivo jar en el tiempo de ejecución

Obviamente, dado que el tiempo de actividad constante es importante, me gustaría agregar la capacidad de volver a cargar las clases existentes (si esto no complica demasiado las cosas).

¿Cuáles son las cosas que debería tener en cuenta? (piense en ello como dos preguntas diferentes: una sobre la recarga de clases en tiempo de ejecución, la otra sobre la adición de nuevas clases).

+0

En lo que respecta a la recarga de clases existentes, examinar esta cuestión relacionada: - http://stackoverflow.com/questions/3216780/problem-reloading-a-jar-using-urlclassloader - http://blogs.oracle .com/CoreJavaTechTips/entry/closing_a_urlclassloader –

Respuesta

79

Recargar las clases existentes con datos existentes es probable que rompa cosas.

puede cargar un nuevo código en nuevos cargadores de clases con relativa facilidad:

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

Cargadores de clases ya no utilizados pueden ser basura recogida (a menos que haya una pérdida de memoria, como suele ser el caso con el uso de ThreadLocal, JDBC controladores, java.beans, etc.).

Si desea conservar los datos del objeto, sugiero un mecanismo de persistencia como la serialización, o lo que sea que esté acostumbrado.

Por supuesto, los sistemas de depuración pueden hacer cosas más elegantes, pero son más extravagantes y menos confiables.

Es posible agregar nuevas clases en un cargador de clases. Por ejemplo, usando URLClassLoader.addURL. Sin embargo, si una clase no se carga (porque, por ejemplo, no la ha agregado), nunca se cargará en esa instancia del cargador de clases.

+0

si mi cargador de clases inicial cargó cosas como grupos de subprocesos y otros recursos, ¿no? ¿significa que las nuevas clases cargadas por el segundo cargador de clases no serán accesibles para esos recursos? –

+0

No he especificado un padre para el URLClassLoader así que solo usará el sistema (lo arreglaré). Eso significa que todavía podrá acceder a las clases en el classpath. Si desea compartir cosas, pase referencias sobre el uso de tipos definidos en cargadores de clases comunes (tal vez el cargador de clases de arranque para Java lib). –

+2

Me falta algo: ¿por qué Class.newInstance() "evil"? –

3

Busqué en Google un poco, y encontramos este código 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); 

puede compartir cualquier opiniones/comentarios/respuestas con respecto a este enfoque?

+1

La URL del jar es para señalar un recurso con un archivo jar.Desea pasar todo el archivo jar a URLClassLoader. (Desafortunadamente los frascos dentro de los frascos están rotos.) –

+1

el enlace está roto. – Koekiebox

+0

enlace fijo (o más bien, googleó "getJarFileToLoadFrom" y encontró otra copia del código anterior a mi pregunta: un enfoque "eventualmente consistente" para "internet") –

8

me pidieron para construir un sistema de java que tendrá la capacidad de cargar nuevo código mientras se ejecuta

Es posible que desee basar su sistema de OSGi (o al menos tomar mucho en ello) , que fue hecho para exactamente esta situación.

Encomendarse con cargadores de clases es un asunto delicado, sobre todo por la visibilidad de clases, y no desea tener problemas posteriores difíciles de solucionar. Por ejemplo, Class.forName(), que se usa ampliamente en muchas bibliotecas, no funciona demasiado bien en un espacio de cargador de clases fragmentado.

+0

con OSGi puedes 1. reemplazar jar en tiempo de ejecución. 2. Mantenga varias versiones del mismo contenedor cuando diferentes partes de su aplicación dependan de las diferentes versiones. 3. otras características como gestión de configuración, fábricas de servicios y mucho más. – Gadi

33

Esto funciona para mí:

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

esto parece funcionar :) – zproxy

+4

Esto no funciona para mí .. – Gynnad

+10

De acuerdo con [Oracle] (http://docs.oracle.com/javase/7/docs/api/java/io/File.html) en su lugar de usar '.toURL()' use '.toURI(). toURL()'. –

2

Uso org.openide.util.Lookup y cargador de clases para cargar dinámicamente el plugin tarro, como se muestra aquí.

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

Te gusta complicado. – Elmue

Cuestiones relacionadas