为什么在Java中这么难做到这一点?如果您希望拥有任何类型的模块系统,您需要能够动态加载JAR文件。有人告诉我,有一种方法可以通过编写自己的ClassLoader来实现,但这需要做很多工作,因为(至少在我看来)应该像调用以JAR文件作为参数的方法一样简单。
对实现这一功能的简单代码有什么建议吗?
为什么在Java中这么难做到这一点?如果您希望拥有任何类型的模块系统,您需要能够动态加载JAR文件。有人告诉我,有一种方法可以通过编写自己的ClassLoader来实现,但这需要做很多工作,因为(至少在我看来)应该像调用以JAR文件作为参数的方法一样简单。
对实现这一功能的简单代码有什么建议吗?
当前回答
请看看我开始的这个项目:proxy-object lib
这个库将从文件系统或任何其他位置加载jar。它将为jar专用一个类装入器,以确保没有库冲突。用户将能够从加载的jar中创建任何对象,并在其上调用任何方法。 这个库被设计用来从支持Java 7的代码库中加载用Java 8编译的jar。
创建一个对象:
File libDir = new File("path/to/jar");
ProxyCallerInterface caller = ObjectBuilder.builder()
.setClassName("net.proxy.lib.test.LibClass")
.setArtifact(DirArtifact.builder()
.withClazz(ObjectBuilderTest.class)
.withVersionInfo(newVersionInfo(libDir))
.build())
.build();
String version = caller.call("getLibVersion").asString();
ObjectBuilder支持工厂方法、调用静态函数和回调接口实现。 我将在自述页面上发布更多的例子。
其他回答
你应该看一看OSGi,比如在Eclipse平台中实现的。它就是这么做的。您可以安装、卸载、启动和停止所谓的包,这些包实际上是JAR文件。但是它做的更多,例如,它提供了可以在运行时在JAR文件中动态发现的服务。
或参阅Java模块系统的规范。
如果你在Android上工作,下面的代码可以工作:
String jarFile = "path/to/jarfile.jar";
DexClassLoader classLoader = new DexClassLoader(jarFile, "/data/data/" + context.getPackageName() + "/", null, getClass().getClassLoader());
Class<?> myClass = classLoader.loadClass("MyClass");
下面的解决方案有点笨拙,因为它使用反射来绕过封装,但它工作得完美无缺:
File file = ...
URL url = file.toURI().toURL();
URLClassLoader classLoader = (URLClassLoader)ClassLoader.getSystemClassLoader();
Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
method.invoke(classLoader, url);
jodonnell提出的解决方案很好,但应该稍加改进。我用这篇文章成功地开发了我的应用程序。
分配当前线程
首先我们要加上
Thread.currentThread().setContextClassLoader(classLoader);
否则你将无法加载资源(如spring/context.xml)存储到jar。
不包括
你的罐子放入父类装入器中,否则你将无法理解谁装入了什么。
请参见使用URLClassLoader重新加载jar的问题
然而,OSGi框架仍然是最好的方法。
我知道我来晚了,但我一直在使用pf4j,这是一个插件框架,它工作得非常好。
PF4J是一个微框架,其目标是保持核心简单但可扩展。
一个插件使用的例子:
在你的应用/插件中使用ExtensionPoint接口标记定义一个扩展点:
public interface Greeting extends ExtensionPoint {
String getGreeting();
}
使用@Extension注释创建一个扩展:
@Extension
public class WelcomeGreeting implements Greeting {
public String getGreeting() {
return "Welcome";
}
}
然后你可以加载和卸载插件,如你所愿:
public static void main(String[] args) {
// create the plugin manager
PluginManager pluginManager = new JarPluginManager(); // or "new ZipPluginManager() / new DefaultPluginManager()"
// start and load all plugins of application
pluginManager.loadPlugins();
pluginManager.startPlugins();
// retrieve all extensions for "Greeting" extension point
List<Greeting> greetings = pluginManager.getExtensions(Greeting.class);
for (Greeting greeting : greetings) {
System.out.println(">>> " + greeting.getGreeting());
}
// stop and unload all plugins
pluginManager.stopPlugins();
pluginManager.unloadPlugins();
}
欲了解更多细节,请参阅文档