使用ServiceLoader动态加载插件jar [英] Dynamically loading plugin jars using ServiceLoader
问题描述
我正在尝试为我的应用程序创建一个插件系统,我想从简单的东西开始。每个插件都应该打包在.jar文件中并实现 SimplePlugin
界面:
I'm trying to create a plugin system for my application, and I want to start with something simple. Every plugin should be packed in a .jar file and implement the SimplePlugin
interface:
package plugintest;
public interface SimplePlugin {
public String getName();
}
现在我已经创建了 SimplePlugin <的实现/ code>,打包在.jar中并将其放在主应用程序的plugin /子目录中:
Now I've created an implementation of SimplePlugin
, packed in a .jar and put it in the plugin/ subdirectory of the main application:
package plugintest;
public class PluginTest implements SimplePlugin {
public String getName() {
return "I'm the plugin!";
}
}
在主应用程序中,我想获得一个实例 PluginTest
。我尝试了两种方法,都使用 java.util.ServiceLoader
。
In the main application, I want to get an instance of PluginTest
. I've tried two alternatives, both using java.util.ServiceLoader
.
1。动态扩展类路径
这使用已知的hack在系统类加载器上使用反射来避免封装,以便添加 URL
是类路径。
This uses the known hack to use reflection on the system class loader to avoid encapsulation, in order to add URL
s the the classpath.
package plugintest.system;
import plugintest.SimplePlugin;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Iterator;
import java.util.ServiceLoader;
public class ManagePlugins {
public static void main(String[] args) throws IOException {
File loc = new File("plugins");
extendClasspath(loc);
ServiceLoader<SimplePlugin> sl = ServiceLoader.load(SimplePlugin.class);
Iterator<SimplePlugin> apit = sl.iterator();
while (apit.hasNext())
System.out.println(apit.next().getName());
}
private static void extendClasspath(File dir) throws IOException {
URLClassLoader sysLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
URL urls[] = sysLoader.getURLs(), udir = dir.toURI().toURL();
String udirs = udir.toString();
for (int i = 0; i < urls.length; i++)
if (urls[i].toString().equalsIgnoreCase(udirs)) return;
Class<URLClassLoader> sysClass = URLClassLoader.class;
try {
Method method = sysClass.getDeclaredMethod("addURL", new Class[]{URL.class});
method.setAccessible(true);
method.invoke(sysLoader, new Object[] {udir});
} catch (Throwable t) {
t.printStackTrace();
}
}
}
添加了plugins /目录正如预期的那样(可以检查调用 sysLoader.getURLs()
),然后是 ServiceLoader
对象给出的迭代器是空的。
The plugins/ directory is added as expected (as one can check calling sysLoader.getURLs()
), but then the iterator given by the ServiceLoader
object is empty.
2。使用URLClassLoader
这使用 ServiceLoader.load
的另一个定义,带有类的第二个参数 ClassLoader
。
This uses another definition of ServiceLoader.load
with a second argument of the class ClassLoader
.
package plugintest.system;
import plugintest.SimplePlugin;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Iterator;
import java.util.ServiceLoader;
public class ManagePlugins {
public static void main(String[] args) throws IOException {
File loc = new File("plugins");
File[] flist = loc.listFiles(new FileFilter() {
public boolean accept(File file) {return file.getPath().toLowerCase().endsWith(".jar");}
});
URL[] urls = new URL[flist.length];
for (int i = 0; i < flist.length; i++)
urls[i] = flist[i].toURI().toURL();
URLClassLoader ucl = new URLClassLoader(urls);
ServiceLoader<SimplePlugin> sl = ServiceLoader.load(SimplePlugin.class, ucl);
Iterator<SimplePlugin> apit = sl.iterator();
while (apit.hasNext())
System.out.println(apit.next().getName());
}
}
再一次,迭代器从来没有下一个元素。
Once again, the iterator has never a "next" element.
我肯定会遗漏一些东西,因为这是我第一次玩类路径并加载。
There's surely something I'm missing since it's the first time I'm "playing" with class paths and loading.
推荐答案
问题很简单。而且很愚蠢。在插件.jar文件中 META-INF
目录中缺少 /services/plugintest.SimplePlugin
文件,所以 ServiceLoader
无法将罐子识别为服务并加载该类。
The problem was very simple. And stupid. In the plugin .jar files the /services/plugintest.SimplePlugin
file was missing inside the META-INF
directory, so the ServiceLoader
couldn't identify the jars as services and load the class.
这几乎是全部,第二个(和更清洁)的方式就像一个魅力。
That's pretty much all, the second (and cleaner) way works like a charm.
这篇关于使用ServiceLoader动态加载插件jar的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!