← 返回首页

Java SPI机制详解

📂 java ⏱ 3 min 424 words

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对于阅读和扩展框架非常重要。