使用 ServiceLoader 动态加载插件 jar

2023-12-27

我正在尝试为我的应用程序创建一个插件系统,并且我想从一些简单的事情开始。每个插件都应该打包在一个 .jar 文件中并实现SimplePlugin界面:

package plugintest;

public interface SimplePlugin {
    public String getName();
}

现在我已经创建了一个实现SimplePlugin,打包成.jar并放入主应用程序的plugin/子目录中:

package plugintest;

public class PluginTest implements SimplePlugin {
    public String getName() {
        return "I'm the plugin!";
    }
}

在主应用程序中,我想获取一个实例PluginTest。我尝试了两种选择,都使用java.util.ServiceLoader.

1.动态扩展类路径

这使用已知的 hack 在系统类加载器上使用反射来避免封装,以便添加URL是类路径。

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对象为空。

2.使用URLClassLoader

这使用了另一个定义ServiceLoader.load与该类的第二个参数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());
    }
}

再说一遍,迭代器从来没有“下一个”元素。

肯定有一些东西我错过了,因为这是我第一次“玩”类路径和加载。


问题很简单。而且愚蠢。在插件 .jar 文件中/services/plugintest.SimplePlugin文件内丢失META-INF目录,所以ServiceLoader无法将 jar 识别为服务并加载该类。

这几乎就是全部,第二种(也是更干净的)方法就像一个魅力。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 ServiceLoader 动态加载插件 jar 的相关文章

随机推荐

  • 如何在 Rxjs 6 中测试 ngrx 管道选择

    以前我可以测试以下商店选择 this store select fromRoot someselector pipe map r gt this store dispatch new Action 这是我的测试中的 provide Stor
  • 如何使用 Java SDK 列出与 AWS Lambda 函数关联的触发器

    在 AWS Lambda 控制台内 如果您单击 触发器 选项卡 如果为该 lambda 函数配置了任何触发器 它将显示触发器列表 如何使用Java SDK for AWS获取配置的触发列表 Thanks 一个简单的 AWS CLI 我知道它
  • 有没有办法让我们自己的应用程序上传其崩溃报告?

    不确定这是否可能 但发生的情况是 有时我们的应用程序会随机崩溃 我们不知道问题到底是什么 有没有办法让我们从用户的 iPhone 上获取崩溃报告 通过代码 还是通过其他应用程序 Thanks 您可以在应用程序中注入第三方代码 这基本上会保存
  • 上一页 窗口焦点 / Electron

    目前我正在为一个棘手的问题而挠头 我刚刚开始使用电子 到目前为止一切顺利 但是 当窗口被隐藏时 它是一个带有快捷方式的弹出窗口 当您按 Enter 键时该窗口就会消失 我想将焦点返回到前一个窗口 我使用的是 Mac 菜单栏显示了我以前的应用
  • 需要帮助弄清楚如何在 Spring Boot Java 应用程序中从 yml 加载嵌套列表

    我有一个像这样的 yaml 文件设置 system locators first com 103 105 second com 105 我想将其加载为 autowired配置看起来像这样 Autowired List
  • Capybara & Rspec:如何删除帐户?

    我正在使用 Devise 并为用户删除自己的帐户的场景编写测试 但我对如何调用确认框并单击 确定 感到困惑 这是链接和我的测试 p p 规范 请求 user spec rb scenario user deletes account do
  • 编写一个读取示例文件的 R 包小插图?

    我正在尝试为 R 中的包编写一个小插图 我一直在关注范德比尔特大学的教程 http biostat mc vanderbilt edu wiki Main HowToCreateAnRPackage也官方文档 http cran r pro
  • 字符数组和指针的区别

    我在两个代码中都做了同样的事情 在代码1中 我使用了char 并使用分配空间malloc in main 在代码2中 我使用了char出于相同目的的数组 但为什么输出不同呢 Code 1 struct node2 int data char
  • 如何检查多个订阅是否达到 onComplete()?

    我在 Angular 6 组件中有两个订阅 现在我想在这两个订阅达到 onComplete 时立即启动一个方法 我怎样才能以最简单的方式做到这一点 Use forkJoin https www learnrxjs io learn rxjs
  • 运行 Qt 单元测试

    看着 并且在另一个 Stackoverflow 答案 https stackoverflow com questions 1524390 qt what unit testing framework 3804999 3804999因为我不想
  • Angular 组件的自定义 CSS 属性

    我有一个封装图片轮播的 Angular 组件 如果使用 host 选择器使自己成为一个 Flexbox 并使用 ngFor 为通过 Input 属性传递给它的图片数组中的每个图片重复一个 img 标签 我有两个相关的问题 1 我想让图片的样
  • Python 和 SSL——EOF 发生违反协议

    我正在尝试通过 Python 登录该网站 我有以下代码 import sys sys path append ClientCookie 1 3 0 import ClientCookie sys path append ClientForm
  • 测试方法不确定:测试未运行。错误?

    我有一个测试类 下面我发布了测试类的示例测试 namespace AdminPortal Tests Controller Test Customer TestClass public class BusinessUnitControlle
  • 聚合具有相同 ID 的行并仅保留 R 中唯一的条目

    我是 R 的初学者 我的 R 数据框如下 Id Values A 0 d Low 5524 Low 6412 Hi 50567 A 0 d Low 5509 Low 6412 Low 6897 Hi 16021 A 0 d Low 5524
  • 在 Sinatra 中间件中访问会话

    我正在开发一个 Sinatra 项目 并在会话中设置了一些变量以供以后使用 我需要帮助的场景是我想访问中间件类中的会话对象 我正在使用典狱长进行身份验证 我想在 Middleware 类中做类似的事情 class MyMiddleware
  • 如何防止 iPhone NSNumberFormatter 舍入?

    我正在尝试像这样格式化大的货币数字 NSNumber testVal NSDecimalNumber decimalNumberWithString 999999999999999993 00 NSNumberFormatter forma
  • 扫描仪 nextLine() 有时会跳过输入

    这是我的代码 Scanner keyboard new Scanner System in System out print Last name lastName keyboard nextLine System out print Fir
  • NSDocument 和 CoreData 是可能的组合,还是 NSPersistentDocument 是唯一的方法?

    我正在制作一个应用程序 为我学校的人们创建课程时间表 这是我对应用程序的粗略设计 我希望我的 NSDocument 子类代表个人的时间表 这个想法是 他们打开一个文档 并且可以将池中的课程添加到他们的时间表中 然后保存 共享 打开等 因此时
  • 如果将最小正浮点值乘以非零数,是否能保证得到非零结果?

    如果我采取epsilon是最小的正非零浮点数 可以是16 32 or 64位 并乘以epsilon由相同大小的非零浮点值 我能保证非零结果 of the 相同的标志和原来一样 价值 或者我是否会冒舍入误差 零或切换符号 的风险 环境 Pyt
  • 使用 ServiceLoader 动态加载插件 jar

    我正在尝试为我的应用程序创建一个插件系统 并且我想从一些简单的事情开始 每个插件都应该打包在一个 jar 文件中并实现SimplePlugin界面 package plugintest public interface SimplePlug