基于SPI的Java插件技术

admin 2024年10月7日18:28:29评论10 views字数 3467阅读11分33秒阅读模式

基于SPI的Java插件技术

最近在弄一个大型的渗透测试辅助工具的项目,正愁插件这里怎么弄呢,因为那种直接调用命令行之类的方法太不优雅了,我实在是不喜欢,正好在翻文章的时候翻到了一个基于SPI的Java插件技术,本着看到需要的东西就要弄个demo实践一下,下面是实践内容

SPI机制

SPI全称为 (Service Provider Interface),是JDK内置的一种服务提供发现机制。工作原理就是ClassPath路径下的META-INF/services文件夹中, 以接口的全限定名来命名文件名,文件里面写该接口的实现。然后再资源加载的方式,读取文件的内容(接口实现的全限定名), 然后再去加载类。SPI可以很灵活的让接口和实现分离, 让api提供者只提供接口, 第三方来实现,使用SPI机制最典型的例子就是Java的数据库驱动。

缺点

虽然ServiceLoader也算是使用的延迟加载,但是基本只能通过遍历全部获取,也就是接口的实现类全部加载并实例化一遍。如果你并不想用某些实现类,它也被加载并实例化了,这就造成了浪费。获取某个实现类的方式不够灵活,只能通过Iterator形式获取,不能根据某个参数来获取对应的实现类。

实现

首先新建我们加载插件的项目,分别有三个文件,提供插件服务的接口IPluginService、插件加载类PluginLoader以及我们的测试主类PluginMain,下面一一列举

IPluginService:

 public interface IPluginService {
 
     /*
      *@Author:RabbitQ
      *@CreateData:2023/8/21 23:07
      *decription:插件功能主入口方法
      */
     void service();
 
     /*
      *@Author:RabbitQ
      *@CreateData:2023/8/21 23:07
      *decription:插件名成,通常用于展示在界面
      */
     String name();
 
 
     /*
      *@Author:RabbitQ
      *@CreateData:2023/8/21 23:07
      *decription:表示当前插件的版本
      */
     String version();
 
 }j

PluginLoader:

 public class PluginLoader {
 
     /*
      *@Author:RabbitQ
      *@CreateData:2023/8/21 23:09
      *decription:插件加载的相对路径:这里表示所有的插件jar都放在主程序jar同级目录的plugins文件夹下
      */
     public static final String PLUGIN_PATH = "plugins";
 
     public static List<IPluginService> loadPlugins() throws MalformedURLException {
         List<IPluginService> plugins = new ArrayList<>();
 
         File parentDir = new File(PLUGIN_PATH);
         File[] files = parentDir.listFiles();
         if (null == files) {
             return Collections.emptyList();
        }
 
         // 从目录下筛选出所有jar文件
         List<File> jarFiles = Arrays.stream(files)
                .filter(file -> file.getName().endsWith(".jar"))
                .collect(Collectors.toList());
 
         URL[] urls = new URL[jarFiles.size()];
         for (int i = 0; i < jarFiles.size(); i++) {
             // 加上 "file:" 前缀表示本地文件
             urls[i] = new URL("file:" + jarFiles.get(i).getAbsolutePath());
        }
 
         URLClassLoader urlClassLoader = new URLClassLoader(urls);
         // 使用 ServiceLoader 以SPI的方式加载插件包中的 IPluginService 实现类
         ServiceLoader<IPluginService> serviceLoader = ServiceLoader.load(IPluginService.class, urlClassLoader);
         for (IPluginService iPluginService : serviceLoader) {
             plugins.add(iPluginService);
        }
         return plugins;
    }
 }

PluginMain:

 public class PluginMain {
     public static void main(String[] args) throws MalformedURLException {
         System.out.println("开始加载插件");
         List<IPluginService> services = PluginLoader.loadPlugins();
         System.out.println(services.size() + "个插件加载成功n");
 
         for (int i = 0; i < services.size(); i++) {
             IPluginService service = services.get(i);
             System.out.println("===插件" + i + "===");
             System.out.println("插件名:" + service.name());
             System.out.println("版本号:" + service.version());
             System.out.println("插件服务启动:");
             service.service();
        }
    }
 }

接下来是我们的测试插件项目,首先把上面的项目要打包成一个jar包添加到测试插件项目的库文件中(这里我是添加到了本地的maven仓库,然后maven引用的,库依赖出了迷之问题,两个方法都ok),因为插件类需要继承我们的IPluginService接口,共有一个文件Plugin1Service,内容为:

 public class Plugin1Service implements IPluginService {
     @Override
     public void service() {
         // 这里可以做插件需要做的任何事情,这里仅用一句打印表示插件的功能被调用
         System.out.println(name() + "功能调用");
    }
 
     @Override
     public String name() {
         return "插件一";
    }
 
     @Override
     public String version() {
         return "1.0";
    }
 }

然后在resource目录下新建META-INF,在META-INF目录下新建services目录,然后新建一个文件,要用你的加载插件类的项目的全限定类名,这里我的是com.rabbitq.IPluginService,内容为你的插件类的全限定类名org.example.Plugin1Service具体情况如下图:

基于SPI的Java插件技术

然后打包你的插件项目并将打包好的jar包放到你的插件加载项目的根目录的plugins目录下,然后运行你的插件加载项目,结果如下:

基于SPI的Java插件技术

上面项目已放在github上,项目代码地址:https://github.com/Rabb1tQ/PluginLoaderUtil

引用:

https://blog.csdn.net/weixin_44155115/article/details/127354183

原文始发于微信公众号(天幕安全团队):基于SPI的Java插件技术

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月7日18:28:29
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   基于SPI的Java插件技术https://cn-sec.com/archives/1969082.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息