Java 如何在运行时加载 jar 文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/194698/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to load a jar file at runtime
提问by Amir Arad
I was asked to build a java system that will have the ability to load new code (expansions) while running. How do I re-load a jar file while my code is running? or how do I load a new jar?
我被要求构建一个能够在运行时加载新代码(扩展)的 Java 系统。如何在代码运行时重新加载 jar 文件?或者我如何加载一个新罐子?
Obviously, since constant up-time is important, I'd like to add the ability to re-load existing classes while at it (if it does not complicate things too much).
显然,由于恒定的正常运行时间很重要,我想添加重新加载现有类的能力(如果它不会使事情变得太复杂)。
What are the things I should look out for? (think of it as two different questions - one regarding reloading classes at runtime, the other regarding adding new classes).
我应该注意哪些事项?(将其视为两个不同的问题 - 一个关于在运行时重新加载类,另一个关于添加新类)。
采纳答案by Tom Hawtin - tackline
Reloading existing classes with existing data is likely to break things.
使用现有数据重新加载现有类可能会破坏事情。
You can load new code into new class loaders relatively easily:
您可以相对轻松地将新代码加载到新的类加载器中:
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();
Class loaders no longer used can be garbage collected (unless there is a memory leak, as is often the case with using ThreadLocal, JDBC drivers, java.beans
, etc).
不再使用的类加载器可以被垃圾收集(除非存在内存泄漏,这在使用 ThreadLocal、JDBC 驱动程序java.beans
等时经常发生)。
If you want to keep the object data, then I suggest a persistence mechanism such as Serialisation, or whatever you are used to.
如果您想保留对象数据,那么我建议使用诸如序列化之类的持久性机制,或者您习惯的任何机制。
Of course debugging systems can do fancier things, but are more hacky and less reliable.
当然,调试系统可以做更漂亮的事情,但更笨拙且更不可靠。
It is possible to add new classes into a class loader. For instance, using URLClassLoader.addURL
. However, if a class fails to load (because, say, you haven't added it), then it will never load in that class loader instance.
可以将新类添加到类加载器中。例如,使用URLClassLoader.addURL
. 然而,如果一个类加载失败(因为,比如说,你没有添加它),那么它永远不会加载到那个类加载器实例中。
回答by Amir Arad
I googled a bit, and found this 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 anyone share opinions/comments/answers regarding this approach?
任何人都可以分享关于这种方法的意见/评论/答案吗?
回答by Thilo
I was asked to build a java system that will have the ability to load new code while running
我被要求构建一个能够在运行时加载新代码的 Java 系统
You might want to base your system on OSGi(or at least take a lot at it), which was made for exactly this situation.
您可能希望将您的系统基于OSGi(或至少在它上面花很多时间),这正是为这种情况而设计的。
Messing with classloaders is really tricky business, mostly because of how class visibility works, and you do not want to run into hard-to-debug problems later on. For example, Class.forName(), which is widely used in many libraries does not work too well on a fragmented classloader space.
弄乱类加载器确实是一件棘手的事情,主要是因为类可见性是如何工作的,而且您不想以后遇到难以调试的问题。例如,在许多库中广泛使用的Class.forName()在碎片化的类加载器空间中效果不佳。
回答by Thilo
This works for 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");
回答by Doan Huynh
Use org.openide.util.Lookup and ClassLoader to dynamically load the Jar plugin, as shown here.
使用 org.openide.util.Lookup 和 ClassLoader 动态加载 Jar 插件,如下所示。
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;
}