繁体   English   中英

Java从目录加载jar文件

[英]Java load jar files from directory

我目前正在开发一个开源项目,人们可以在其中添加自己的.jar来扩展所包含的功能。 但是,我对如何在jar中加载类感到困惑。

我有一个抽象类,它具有一个抽象方法onEnable()和一些getter,该方法提供了一些可与应用程序一起使用的对象。 插件需要我的插件类BasePlugin的子类。 该jar应该添加到/ plugins中,因此我希望在应用程序启动时加载/ plugins文件夹中的所有* .jar文件。

我现在遇到的问题是,在找到的所有方法中,我需要在jar文件中声明类的类路径,这是我所不知道的。 我也不知道jar文件的名称。 因此,我需要扫描/ plugins文件夹中的任何* .jar文件,并将相应的类加载到实现BasePlugin的jar中,并调用onEnable()方法。

基本想法也是...

  1. 读取特定目录中的所有文件
  2. File引用转换为每个结果的URL
  3. 使用URLClassLoader (以URL结果作为种子)来加载每个结果
  4. 使用URLClassLoader#findResources查找具有特定名称的所有匹配资源
  5. 遍历匹配的资源并加载每个资源,至少应提供“入口点”类名
  6. 加载“入口点”属性指定的类

例如...

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:我还没有运行,但这是“基本

SpigotMC还将JAR文件用作插件,在jar中是一个plugin.yaml文件,用于存储有关插件的额外信息,包括类路径。 您不需要使用YAML文件,而可以使用JSON甚至纯文本文件之类的东西。

YAML文件位于jar内,可以使用此处说明的某些方法进行访问。 然后,您可以获取classpath属性,然后使用此处说明的方法加载jar。 可以存储有关插件的其他信息,例如名称,版本和相关性。

Java已经为此提供了一个类: ServiceLoader

Java 6引入了ServiceLoader类,但是“ 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来扫描plugins

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引入的一种更完整的代码封装形式,可提高安全性。

有一个例子在这里可能会有所帮助。 另外,您应该看看OSGi。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM