Java的SPI机制——动态加载ServiceLoader

举报
早点下班 发表于 2025/12/07 15:34:41 2025/12/07
【摘要】 动态加载ServiceLoader ServiceLoader 核心作用ServiceLoader 是 Java 内置的服务提供者加载工具(属于 java.util 包),核心目标是实现 SPI(Service Provider Interface,服务提供者接口)机制:解耦服务接口与实现类,让程序能动态发现、加载和使用符合规范的第三方 / 模块化服务实现,无需硬编码依赖,支持可插拔的扩展...

动态加载ServiceLoader

ServiceLoader 核心作用

ServiceLoader 是 Java 内置的服务提供者加载工具(属于 java.util 包),核心目标是实现 SPI(Service Provider Interface,服务提供者接口)机制解耦服务接口与实现类,让程序能动态发现、加载和使用符合规范的第三方 / 模块化服务实现,无需硬编码依赖,支持可插拔的扩展。

ServiceLoader 使用示例

  1. 定义一个通用接口Driver

    public interface Driver {
    
        void init();
    
    }
    
  2. Driver的不同实现DriverImpl1DriverImpl2

    public class DriverImpl1 implements Driver{
        @Override
        public void init() {
            System.out.println("DriverImpl1 init");
        }
    }
    
    public class DriverImpl2 implements Driver{
        @Override
        public void init() {
            System.out.println("DriverImpl2 init");
        }
    }
    
    
  3. src/main/resources/META-INF/services下新建SPI规范文件,文件名=接口的全类名,如com.xxx.Driver

    com.xxx.DriverImpl1
    com.xxx.DriverImpl2
    
  4. 使用ServiceLoader 加载Driver的不同实现DriverImpl1DriverImpl2实例

    public class App {
    
        public static void main(String[] args) {
            ServiceLoader<Driver> serviceLoader = ServiceLoader.load(Driver.class);
            for (Driver driver : serviceLoader) {
                driver.init();
            }
        }
    }
    

    输出

    DriverImpl1 init
    DriverImpl2 init
    

ServiceLoader 典型使用场景

典型使用场景

  1. JDBC 驱动加载(最经典):
    • Java 核心库只定义 java.sql.Driver 接口,不提供具体数据库驱动;
    • MySQL/Oracle 等厂商实现 Driver 接口,并在 META-INF/services/java.sql.Driver 文件中写入实现类名;
    • 程序通过 ServiceLoader.load(Driver.class) 自动加载驱动,无需手动注册。
  2. 日志框架扩展(如 SLF4J):
    • SLF4J 定义日志接口,logback/log4j 作为实现;
    • 引入 logback 依赖后,ServiceLoader 自动加载其实现类,程序无需修改日志调用代码。
  3. 框架扩展(如 Dubbo/SpringBoot):
    • Dubbo 通过 ServiceLoader 加载协议、序列化器等扩展;
    • SpringBoot 的自动配置部分也依赖 SPI 机制加载第三方 Starter。

JDBC驱动加载场景

在JDK中定义了数据库驱动的接口规范java.sql.Driver,常见的数据库驱动厂商分别有不同的实现,如Mysql的驱动常用的有com.mysql:mysql-connector-j或者org.mariadb.jdbc:mariadb-java-client分别实现了java.sql.Driver

java_sql_Driver
com_mysql_cj_jdbc_Driver
org_mariadb_jdbc_Driver

并在他们的jar中提供了基于SPI规范的META-INF

|---mysql-connector-j
	|---com.mysql...
	|---META-INF
		|---services
			|---java.sql.Driver ## 内容为 com.mysql.cj.jdbc.Driver
|---mariadb-java-client
	|---org.mariadb...
	|---META-INF
		|---services
			|---java.sql.Driver ## 内容为 org.mariadb.jdbc.Driver

在实际使用中,一些驱动管理组件会调用ServiceLoader.load(Driver.class)去加载这些遵循SPI规范的驱动实例。如所有的java原生的java.sql.DriverManager中会调用确保数据库驱动加载,而Driver的实现类中都有静态代码块注册数据库驱动

/** MariaDB Driver */
public final class Driver implements java.sql.Driver {
    // 使用静态代码自动注册驱动
	static {
        try {
          	DriverManager.registerDriver(new Driver());
        } catch (SQLException e) {
          	// eat
        }
  	}
}

public class DriverManager {
    //...
    // 获取数据库驱动前确保驱动已经被加载
    private static void ensureDriversInitialized() {
        //... 
		/**
		*  	基于SPI协议使用ServiceLoad加载所有的数据库驱动
		*	适配java安全模式启动,此处加载数据库驱动需要使用AccessController临时提升权限	
		*/
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
				// 加载所有的数据库驱动
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                // 获取数据库驱动迭代器
                Iterator<Driver> driversIterator = loadedDrivers.iterator();
                try {
                    while (driversIterator.hasNext()) {
                        // 遍历驱动器时,驱动中的静态代码块会自动注册
                        driversIterator.next();
                    }
                } catch (Throwable t) {
                    // Do nothing
                }
                return null;
            }
        });
    }
}

ServiceLoader 核心价值

  1. 解耦接口与实现,降低代码耦合

    • 服务接口(如 java.sql.Driver)由核心框架 / 模块定义,实现类(如 MySQL/Oracle 驱动)由第三方提供;

    • 核心代码仅依赖接口,不直接引用实现类,避免了 “硬编码 new 实现类” 的耦合问题,修改 / 替换实现无需改动核心代码。

  2. 动态加载实现类(运行时扩展)

    ServiceLoader 会自动扫描类路径下的特定配置文件,加载所有符合规范的实现类,无需手动管理类加载:

    • 配置文件路径:META-INF/services/[服务接口全限定名]

    • 配置文件内容:每行写一个实现类的全限定名(支持多个实现)。

  3. 支持可插拔的扩展机制

    这是 ServiceLoader 最核心的价值:第三方只需遵循 SPI 规范实现接口、配置文件,无需修改原有程序,即可无缝接入扩展。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。