Java从目录加载jar文件

时间:2019-04-02 01:12:32

标签: java

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

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

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

4 个答案:

答案 0 :(得分:2)

基本思想也是...

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

答案 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。