Java SPI机制详解
Java SPI机制详解
什么是SPI
SPI(Service Provider Interface)是Java提供的一种服务发现机制。它允许第三方为某个接口提供实现,通过配置文件来注册实现类。
SPI工作原理
1. 定义接口(服务提供者)
2. 编写实现类
3. 在META-INF/services/目录下创建配置文件
4. 通过ServiceLoader加载实现
基础示例
定义接口
public interface DatabaseDriver {
String getName();
Connection connect(String url);
void disconnect(Connection conn);
}
实现类
public class MysqlDriver implements DatabaseDriver {
@Override
public String getName() {
return "MySQL";
}
@Override
public Connection connect(String url) {
System.out.println("连接MySQL: " + url);
return null;
}
@Override
public void disconnect(Connection conn) {
System.out.println("断开MySQL连接");
}
}
public class PostgresDriver implements DatabaseDriver {
@Override
public String getName() {
return "PostgreSQL";
}
@Override
public Connection connect(String url) {
System.out.println("连接PostgreSQL: " + url);
return null;
}
@Override
public void disconnect(Connection conn) {
System.out.println("断开PostgreSQL连接");
}
}
SPI配置文件
文件路径:META-INF/services/com.example.DatabaseDriver
com.example.MysqlDriver
com.example.PostgresDriver
加载实现
import java.util.ServiceLoader;
public class SpiDemo {
public static void main(String[] args) {
ServiceLoader<DatabaseDriver> drivers =
ServiceLoader.load(DatabaseDriver.class);
for (DatabaseDriver driver : drivers) {
System.out.println("加载驱动: " + driver.getName());
Connection conn = driver.connect("jdbc:mysql://localhost:3306/db");
}
}
}
自定义SPI加载器
public class SpiLoader<T> {
private final Class<T> serviceInterface;
private final Map<String, T> providers = new ConcurrentHashMap<>();
public SpiLoader(Class<T> serviceInterface) {
this.serviceInterface = serviceInterface;
}
public void load() {
String serviceName = serviceInterface.getName();
String path = "META-INF/services/" + serviceName;
try (InputStream is = Thread.currentThread()
.getContextClassLoader().getResourceAsStream(path)) {
if (is == null) return;
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
String className;
while ((className = reader.readLine()) != null) {
className = className.trim();
if (!className.isEmpty() && !className.startsWith("#")) {
try {
Class<?> clazz = Class.forName(className);
T provider = serviceInterface.cast(clazz.getDeclaredConstructor().newInstance());
providers.put(className, provider);
} catch (Exception e) {
throw new RuntimeException("加载SPI实现失败: " + className, e);
}
}
}
}
}
public T getProvider(String name) {
return providers.get(name);
}
public Collection<T> getAllProviders() {
return providers.values();
}
}
// 使用
SpiLoader<DatabaseDriver> loader = new SpiLoader<>(DatabaseDriver.class);
loader.load();
DatabaseDriver mysql = loader.getProvider("com.example.MysqlDriver");
Java中的SPI应用
JDBC驱动加载
// java.sql.Driver接口通过SPI机制加载
// META-INF/services/java.sql.Driver文件内容:
// com.mysql.cj.jdbc.Driver
// org.postgresql.Driver
// 使用时只需指定URL,驱动自动加载
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/db", "user", "password");
日志框架SPI
// SLF4J通过SPI加载日志实现
// META-INF/services/org.slf4j.spi.SLF4JServiceProvider
// ch.qos.logback.classic.spi.LogbackServiceProvider
Spring Boot中的SPI
// Spring Boot使用spring.factories替代META-INF/services
// 文件路径:META-INF/spring.factories
// 内容示例:
// org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
// com.example.MyAutoConfiguration
@AutoConfiguration
public class MyAutoConfiguration {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
增强版SPI
public class EnhancedSpiLoader<T> {
private final Class<T> serviceInterface;
private final Map<String, Supplier<T>> factories = new ConcurrentHashMap<>();
public EnhancedSpiLoader(Class<T> serviceInterface) {
this.serviceInterface = serviceInterface;
}
public EnhancedSpiLoader<T> register(String name, Supplier<T> factory) {
factories.put(name, factory);
return this;
}
public T create(String name) {
Supplier<T> factory = factories.get(name);
if (factory == null) {
throw new IllegalArgumentException("未知实现: " + name);
}
return factory.get();
}
public List<T> createAll() {
return factories.values().stream()
.map(Supplier::get)
.collect(Collectors.toList());
}
}
// 使用
EnhancedSpiLoader<Serializer> loader = new EnhancedSpiLoader<>(Serializer.class);
loader.register("json", JsonSerializer::new);
loader.register("xml", XmlSerializer::new);
loader.register("yaml", YamlSerializer::new);
Serializer json = loader.create("json");
总结
SPI机制是Java实现插件化和可扩展设计的基础。它解耦了接口定义和实现,允许在不修改主程序的情况下添加新功能。理解SPI对于阅读和扩展框架非常重要。