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

查看:17
本文介绍了使用 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 的实现,打包在一个 .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 文件中,/services/plugintest.SimplePlugin 文件在 META-INF 目录中丢失,因此 ServiceLoader 不能将 jar 识别为服务并加载类.

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天全站免登陆