Java SPI 与 ServiceLoader
是什么 SPI(Service Provider Interface,服务提供者接口)是 Java 内置的插件机制:允许第三方在不修改调用方代码的前提下,提供接口的实现。 核心思路一句话: 调用方只依赖接口 → 实现由 ServiceLoader 在运行时发现 调用方编译期只认接口,不写死任何实现类;具体用哪个实现,由 classpath 上的实现库通过约定文件「自报家门」,由 java.util.ServiceLoader 在运行时加载并实例化。这与 IoC 的思想类似——把装配的控制权移到程序之外,在模块化设计里尤其重要。 三个组成部分 组成 说明 接口(Service) 由框架或标准库定义,如 java.sql.Driver 实现(Provider) 由第三方库提供,如 com.mysql.cj.jdbc.Driver 注册文件 META-INF/services/<接口全限定名>,内容是实现类的全限定名 注册文件规则: 文件名是服务接口的完全限定二进制名称 每行一个实现类的全限定名;空行、行首 # 注释会被忽略 文件必须使用 UTF-8 编码 提供者类必须有无参构造方法,以便被反射实例化 工作流程 ① 调用方:ServiceLoader.load(Driver.class) ↓ ② JVM 扫描 classpath 上所有 jar 里的 META-INF/services/java.sql.Driver ↓ ③ 读出文件中的实现类名,反射实例化 ↓ ④ 返回一个可迭代所有实现的迭代器 ServiceLoader 以延迟方式查找并实例化:维护已加载实现的缓存,iterator() 先返回缓存中的元素,再按需查找剩余实现并依次加入缓存,可用 reload() 清缓存。 一个完整例子 接口与两个实现: package com.example.api; public interface Greeting { void sayHi(); } public class GreetingBeijing implements Greeting { public void sayHi() { System.out.println("hi from beijing"); } } public class GreetingZhengzhou implements Greeting { public void sayHi() { System.out.println("hi from zhengzhou"); } } 注册文件 src/main/resources/META-INF/services/com.example.api.Greeting: ...