使用ServiceLoader动态加载插件jar [英] Dynamically loading plugin jars using ServiceLoader

查看:291
本文介绍了使用ServiceLoader动态加载插件jar的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为我的应用程序创建一个插件系统,我想从简单的东西开始。每个插件都应该打包在.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 URLs 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆