目 录CONTENT

文章目录

一文搞懂 Spring 的 SPI 机制

在等晚風吹
2024-11-28 / 0 评论 / 0 点赞 / 18 阅读 / 0 字 / 正在检测是否收录...

一文搞懂 Spring 的 SPI 机制


什么是 Java SPI?

SPI 的全称是 Service Provider Interface,它是 Java 提供的一套用于扩展或替换组件的机制。SPI 的主要作用是通过接口编程、策略模式和配置文件,动态加载具体实现,从而实现:

  • 解耦:接口和实现分离。
  • 高扩展性:支持第三方实现,达到插件式扩展效果。

Java SPI 示例

1. 定义接口

public interface HelloSpi {
    String getName();
    void handle();
}

2. 定义实现类

public class OneHelloSpiImpl implements HelloSpi {
    @Override
    public String getName() {
        return "One";
    }
    @Override
    public void handle() {
        System.out.println(getName() + "执行");
    }
}

public class TwoHelloSpiImpl implements HelloSpi {
    @Override
    public String getName() {
        return "Two";
    }
    @Override
    public void handle() {
        System.out.println(getName() + "执行");
    }
}

3. 配置文件

META-INF/services 目录下创建文件,文件名为接口的全类名,内容为实现类的全类名:

com.shuaijie.impl.OneHelloSpiImpl
com.shuaijie.impl.TwoHelloSpiImpl

4. 测试代码

使用 ServiceLoader 加载并执行服务:

@Test
public void testSpi() {
    ServiceLoader<HelloSpi> load = ServiceLoader.load(HelloSpi.class);
    Iterator<HelloSpi> iterator = load.iterator();
    while (iterator.hasNext()) {
        HelloSpi next = iterator.next();
        System.out.println(next.getName() + " 准备执行");
        next.handle();
    }
    System.out.println("执行结束");
}

5. 执行结果

One 准备执行
One执行
Two 准备执行
Two执行
执行结束

通过结果可以看出,HelloSpi 接口的所有实现类都得到了调用。


ServiceLoader 原理解析

ServiceLoader 是一个简单的服务提供者加载工具,由 JDK6 引入。
关键代码如下:

public static <S> ServiceLoader<S> load(Class<S> service) {
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    return ServiceLoader.load(service, cl);
}

load 方法通过 线程上下文类加载器 加载配置文件,这破坏了双亲委派模型(可以作为面试知识点)。
hasNext() 方法通过加载 META-INF/services 路径下的配置文件,获取实现类的全类名:

private static final String PREFIX = "META-INF/services/";
private boolean hasNextService() {
    // 省略其他代码
    String fullName = PREFIX + service.getName();
    configs = loader.getResources(fullName);
    pending = parse(service, configs.nextElement());
    nextName = pending.next();
    return true;
}

常见使用场景

许多框架和库使用了 SPI 机制,例如:

  • JDBC
  • Dubbo
  • Logback
  • Spring

什么是 Spring SPI?

Spring 对 Java SPI 进行了封装和增强。通过在 META-INF/spring.factories 中配置接口的实现类名,可以在运行时动态加载接口实现。

示例:使用 Spring SPI 实现服务加载

1. 配置文件

META-INF/spring.factories 中添加:

com.shuaijie.impl.OneHelloSpiImpl
com.shuaijie.impl.TwoHelloSpiImpl

2. 测试代码

@Test
public void testSpringSpi() {
    List<HelloSpi> helloSpiList = SpringFactoriesLoader.loadFactories(HelloSpi.class, this.getClass().getClassLoader());
    Iterator<HelloSpi> iterator = helloSpiList.iterator();
    while (iterator.hasNext()) {
        HelloSpi next = iterator.next();
        System.out.println(next.getName() + " 准备执行");
        next.handle();
    }
    System.out.println("执行结束");
}

3. 执行结果

One 准备执行
One执行
Two 准备执行
Two执行
执行结束

Spring Boot Starter 实现示例

Spring Boot 的 Starter 依赖是通过 Spring SPI 实现的。这种机制允许我们创建自定义 Starter,为项目提供模块化功能。

1. 编写接口和实现

接口:

package com.shuaijie.service;
public interface SpringbootStarterService {
    void handle();
}

实现:

package com.shuaijie.service.impl;
public class SpringbootStarterServiceImpl implements SpringbootStarterService {
    @Override
    public void handle() {
        System.out.println("SpringbootStarterServiceImpl执行");
    }
}

2. 配置 spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.shuaijie.service.impl.SpringbootStarterServiceImpl

3. 定义 Maven 配置

pom.xml 中指定 groupIdartifactIdversion

<groupId>com.shuaijie</groupId>
<artifactId>spring-boot-starter-shuaijie</artifactId>
<version>1.0.0</version>

4. 测试自定义 Starter

引入 Starter 后,在 Spring Boot 项目中直接使用:

@Autowired
private SpringbootStarterService springbootStarterService;

@Test
public void testSpringbootStarter() {
    springbootStarterService.handle();
}

总结

通过 SPI 机制,Java 和 Spring 提供了强大的扩展能力。理解并灵活运用 SPI,可以极大地提高项目的模块化和可扩展性,同时也能为团队开发提供便利。

0

评论区