java实现插拔式加载用户自定义插件

 更新时间:2025年09月16日 10:13:40   作者:Java八股文  
本文主要介绍了DatalinkX通过自定义URLClassLoader实现插件化加载外部数据源jar包,突破SPI机制依赖限制,使用户无需修改源码即可扩展数据源同步功能,达成动态插拔效果

前言

从23年底开始维护自己的开源项目,异构数据源流转系统datalinkx,系统支持不同数据源之间的数据同步,包括mysql、oracle、es、redis等等。

有一天一个同学问我说datalinkx系统支不支持用户上传自己的自定义数据源插件,不用动datalinkx的源码即可让系统支持用户自定义数据源的读写同步呢。

我一听有点意思,一般我都是版本迭代后支持某个数据源的读写,能不能把这个能力开放出去呢,这样大家使用的时候就不需要等系统更新,上传符合规范的插件包就可以。

也就是说我们需要实现,用户只需要将其他数据源的同步jar包,放到某个文件夹下,系统就会自动扫描到这个jar包从而获得对应数据源的读写能力。

SPI

通常听到插件化就会下意识的想起SPI,SPI是JDK内置的一种类似服务提供发现的机制,比如java.sql.Driver接口,其他不同的数据源厂商可以针对这一接口做出不同的实现, MySQL和PostgreSQL分别有不同的实现提供给用户,利用SPI机制可以为某个接口寻找服务实现。

主要思想是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要,核心思想就解耦,具体实现可以参考jdbc的实现。

思考下利用SPI可以实现插拔式插件开发吗,似乎不可以,使用SPI必须在当前classpath中,也就是说虽然解耦了,但必须存在依赖,比如你想要让系统支持PG,必须要在项目中引入PG驱动依赖才可以加载到PG驱动内的相关类。

这样还是要修改系统源代码才能得到支持,这明显是不符合我们的预期的。

自定义加载器

我们想要的效果是用户可以开发一个独立的插件jar包,放到datalinkx的driver-dist目录下,datalinkx即可获得独立插件jar包中的能力。

看到这里有准备面试的jym心里默背一遍java类加载过程和双亲委派机制,背不下来的看完赶紧补上。我们知道一个JVM进程正常情况下只能加载当前classpath下的所有java类。

有没有通过扫描固定路径下的jar包,将外部java类加载当当前classpath的方式呢。有的兄弟,有的。

URLClassLoader

// 从driver-dist文件夹下读取各个数据源插件
List<URL> dataSourcePlugins = JarLoaderUtil.loadJarsFromDirectory(DRIVER_DIST);
String targetJarName = String.format("datalinkx-driver-%s-1.0.0.jar", dsType.toLowerCase());
String targetClassName = getDriverClass(dsType);

URL targetJarUrl = dataSourcePlugins.stream()
        .filter(plugin -> plugin.getPath().endsWith(targetJarName))
        .findFirst()
        .orElseThrow(() -> new DatalinkXServerException(StatusCode.DRIVER_LOAD_FAIL, "系统未支持该数据源类型"));

// 使用自定义的 URLClassLoader 加载目标 JAR 包中的类
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{targetJarUrl}, Thread.currentThread().getContextClassLoader());
Class<?> clazz = Class.forName(targetClassName, true, urlClassLoader);

我们总结一下具体做了啥:

  1. 各个数据源模块打包后都会自动复制到datalinkx/driver-dist中
  2. 通过使用自定义 URLClassLoader加载器加载driver-dist文件夹内的jar包
  3. 通过class.forName反射的方式生成对用对象

这样我们就可以在当前进程中加载到外部jar包中的类,也就实现了Java插拔式加载用户自定义插件功能。

到此这篇关于java实现插拔式加载用户自定义插件的文章就介绍到这了,更多相关java 插拔式加载自定义插件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:

相关文章

  • SpringBoot生成和操作PDF的代码详解

    SpringBoot生成和操作PDF的代码详解

    本文主要介绍了在SpringBoot项目下,通过代码和操作步骤,详细的介绍了如何操作PDF,希望可以帮助到准备通过JAVA操作PDF的你,项目框架用的SpringBoot,但在JAVA中代码都是通用的,需要的朋友可以参考下
    2025-01-01
  • Java lambda list转换map时,把多个参数拼接作为key操作

    Java lambda list转换map时,把多个参数拼接作为key操作

    这篇文章主要介绍了Java lambda list转换map时,把多个参数拼接作为key操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08
  • 如何理解SpringMVC

    如何理解SpringMVC

    Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发
    2021-06-06
  • java实现Redisson的基本使用

    java实现Redisson的基本使用

    Redisson是一个在Redis的基础上实现的Java驻内存数据网格客户端,本文主要介绍了java实现Redisson的基本使用,具有一定的参考价值,感兴趣的可以了解一下
    2023-12-12
  • springboot之配置双kafka全过程

    springboot之配置双kafka全过程

    这篇文章主要介绍了springboot之配置双kafka全过程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • java实现Excel的导入导出

    java实现Excel的导入导出

    这篇文章主要为大家详细介绍了java实现Excel的导入导出,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-06-06
  • springboot+springmvc+mybatis项目整合

    springboot+springmvc+mybatis项目整合

    这篇文章主要为大家详细介绍了springboot+springmvc+mybatis项目的整合,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-04-04
  • Java聊天室之使用Socket实现传递对象

    Java聊天室之使用Socket实现传递对象

    这篇文章主要为大家详细介绍了Java简易聊天室之使用Socket实现传递对象功能,文中的示例代码讲解详细,具有一定的借鉴价值,需要的可以了解一下
    2022-10-10
  • SpringBoot启动失败Application run failed的问题分析及解决办法

    SpringBoot启动失败Application run failed的问题分析及解决办法

    文章主要介绍了SpringBoot启动失败的问题分析和解决办法,包括XML文件格式错误、数据库名称错误以及MyBatis扫描路径问题的排查和修复步骤,感兴趣的朋友跟随小编一起看看吧
    2026-01-01
  • 分析ThreadLocal内存泄漏问题

    分析ThreadLocal内存泄漏问题

    ThreadLocal的作用是提供线程内的局部变量,这种变量在线程生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量传递的复杂度,但是如果滥用ThreadLocal可能会导致内存泄漏,所以本文将为大家分析ThreadLocal内存泄漏问题
    2023-07-07

最新评论