que - java jar classpath
Cómo cargar un archivo jar en tiempo de ejecución (5)
Esta pregunta ya tiene una respuesta aquí:
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?
Obviamente, dado que el tiempo de actividad constante es importante, me gustaría agregar la capacidad de volver a cargar las clases existentes mientras lo hace (si 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).
Me pidieron que construyera un sistema Java que tenga la capacidad de cargar código nuevo mientras se ejecuta
Es posible que desee basar su sistema en OSGi (o al menos tomar mucho en ello), que se hizo para exactamente esta situación.
Enfrentar a los cargadores de clases es una tarea complicada, principalmente por la forma en que funciona la visibilidad de clase, y no querrá encontrarse con problemas difíciles de depurar más adelante. Por ejemplo, Class.forName() , que se usa ampliamente en muchas bibliotecas, no funciona demasiado bien en un espacio de cargador de clases fragmentado.
Busqué en Google un poco y encontré 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);
¿Alguien puede compartir opiniones / comentarios / respuestas con respecto a este enfoque?
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");
Recargar las clases existentes con datos existentes es probable que rompa cosas.
Puede cargar código nuevo 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();
Los cargadores de clase que ya no se utilizan pueden ser recogidos como basura (a menos que haya una pérdida de memoria, como suele ser el caso con el uso de ThreadLocal, controladores JDBC, java.beans
, etc.).
Si desea conservar los datos del objeto, sugiero un mecanismo de persistencia, como 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 maliciosos 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.
Use org.openide.util.Lookup y ClassLoader para cargar dinámicamente el plugin Jar, 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;
}