gpt4 book ai didi

java - 使用 ServiceLoader 动态加载插件 jar

转载 作者:IT老高 更新时间:2023-10-28 20:52:00 26 4
gpt4 key购买 nike

我正在尝试为我的应用程序创建一个插件系统,我想从一些简单的东西开始。每个插件都应该打包在一个 .jar 文件中并实现 SimplePlugin 接口(interface):

package plugintest;

public interface SimplePlugin {
public String getName();
}

现在我创建了一个 SimplePlugin 的实现,打包在一个 .jar 中,并将它放在主应用程序的 plugin/子目录中:

package plugintest;

public class PluginTest implements SimplePlugin {
public String getName() {
return "I'm the plugin!";
}
}

在主应用程序中,我想获取 PluginTest 的实例。我尝试了两种选择,都使用 java.util.ServiceLoader.

1.动态扩展类路径

这使用已知的 hack 在系统类加载器上使用反射来避免封装,以便在类路径中添加 URL

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 对象给出的迭代器为空。

<强>2。使用 URLClassLoader

这使用 ServiceLoader.load 的另一个定义和类 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());
}
}

再一次,迭代器从来没有“下一个”元素。

我肯定缺少一些东西,因为这是我第一次“玩”类路径和加载。

最佳答案

问题很简单。而且很愚蠢。在插件 .jar 文件中,META-INF 目录中缺少 /services/plugintest.SimplePlugin 文件,因此 ServiceLoader 不能将 jar 识别为服务并加载类。

差不多就是这样,第二种(更简洁的)方式就像一个魅力。

关于java - 使用 ServiceLoader 动态加载插件 jar,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16102010/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com