我目前正在开发一个开源项目,人们可以在其中添加自己的.jar来扩展所包含的功能。但是,我对如何将类加载到jar中感到困惑。
我有一个抽象类,它有一个抽象方法onEnable()
和一些提供某些对象以与应用程序一起工作的getter。该插件需要我的插件类BasePlugin
的子类。该jar应该添加到/ plugins中,因此我希望在应用程序启动时加载/ plugins文件夹中的所有* .jar文件。
我现在遇到的问题是,在我发现的所有方法中,我需要在jar文件中声明类的类路径,这是我所不知道的。我也不知道jar文件的名称。因此,我需要在/ plugins文件夹中扫描任何* .jar文件,并将相应的类加载到实现BasePlugin
并调用onEnable()
方法的jar中。
答案 0 :(得分:2)
基本思想也是...
File
引用转换为URL
URLClassLoader
结果为种子的URL
来加载每个结果URLClassLoader#findResources
查找具有特定名称的所有匹配资源例如...
public List<PluginClass> loadPlugins() throws MalformedURLException, IOException, ClassNotFoundException {
File plugins[] = new File("./Plugins").listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
return file.getName().endsWith(".jar");
}
});
List<URL> plugInURLs = new ArrayList<>(plugins.length);
for (File plugin : plugins) {
plugInURLs.add(plugin.toURI().toURL());
}
URLClassLoader loader = new URLClassLoader(plugInURLs.toArray(new URL[0]));
Enumeration<URL> resources = loader.findResources("/META-INFO/Plugin.properties");
List<PluginClass> classes = new ArrayList<>(plugInURLs.size());
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
Properties properties = new Properties();
try (InputStream is = resource.openStream()) {
properties.load(is);
String className = properties.getProperty("enrty-point");
PluginClass pluginClass = loader.loadClass(className);
classes.add(pluginClass);
}
}
return classes
}
nb:我还没有运行它,但这是“基本的
答案 1 :(得分:1)
SpigotMC也使用JAR文件作为插件,jar内是plugin.yaml
文件,用于存储有关插件的额外信息,包括类路径。您不需要使用YAML文件,而可以使用JSON之类的文件,甚至可以使用纯文本文件。
YAML文件位于jar内,可以使用here中介绍的某些方法进行访问。然后,您可以获取classpath属性,然后使用介绍的here方法加载jar。可以存储有关插件的其他信息,例如名称,版本和依赖项。
答案 2 :(得分:1)
Java已经为此提供了一个类:ServiceLoader。
ServiceLoader类是Java 6引入的,但“ SPI jar”概念实际上与Java 1.3一样古老。想法是每个jar都包含一个简短的文本文件,该文件描述了其对特定服务提供商接口的实现。
例如,如果一个.jar文件包含两个名为FooPlugin和BarPlugin的BasePlugin子类,则.jar文件还将包含以下条目:
META-INF/services/com.example.BasePlugin
该jar条目将是一个文本文件,其中包含以下几行:
com.myplugins.FooPlugin
com.myplugins.BarPlugin
您的项目将通过创建从plugins
目录读取的ClassLoader来扫描插件:
Collection<URL> urlList = new ArrayList<>();
Path pluginsDir = Paths.get(
System.getProperty("user.home"), "plugins");
try (DirectoryStream<Path> jars =
Files.newDirectoryStream(pluginsDir, "*.jar")) {
for (Path jar : jars) {
urlList.add(jar.toUri().toURL());
}
}
URL[] urls = urlList.toArray(new URL[0]);
ClassLoader pluginClassLoader = new URLClassLoader(urls,
BasePlugin.class.getClassLoader());
ServiceLoader<BasePlugin> plugins =
ServiceLoader.load(BasePlugin.class, pluginClassLoader);
for (BasePlugin plugin : plugins) {
plugin.onEnable();
// etc.
}
使用ServiceLoader的另一个优点是您的代码将能够使用模块,这是Java 9引入的一种更完整的代码封装形式,可提供更高的安全性。
答案 3 :(得分:0)
有一个示例here,可能会有所帮助。另外,您应该看看OSGi。