文章目录
2.4 SPI机制(Service Provider Interface)
- 问题:依赖倒转原则提到,应该依赖接口而不是实现类,但接口最终要有实现类落地。如果因为业务调整需要替换某个接口的实现类,就不得不改动实现类,也就是修改源码。
- 解决:SPI机制解决了这个问题。通过一种“服务寻找”的机制,动态地加载接口/抽象类对应的具体实现类。把接口的具体实现类的定义和声明交给了外部化的配置文件。
- 如下图,一个接口可以有多个实现类,通过SPI机制,可以将一个接口需要创建的实现类的对象都罗列到一个特殊的文件中,SPI机制会依次将这些实现类的对象进行创建并返回。
2.4.1 JDK原生SPI
简单了解即可,使用范围有限,只能通过接口或抽象类来加载具体的实现类。
1.定义接口+实现类
模拟一套Dao接口的不同数据库访问支持
public interface DemoDao {
}
public class DemoMysqlDao implements DemoDao {
}
public class DemoOracleDao implements DemoDao {
}
2.声明SPI文件
JDK的SPI需要遵循以下规范:
- 所有定义的SPI文件都必须放在项目的META-INF/services目录下
- 文件名必须命名为接口或抽象类的全限定名
- 文件内容为接口或抽象类的具体实现类的全限定名;如果有多个,则每行声明一个具体实现类的全限定名,多个类之间没有分隔符
具体步骤:
(1)在resources目录下创建新目录META-INF/services
(2)新建文件:com.star.springboot.spi.DemoDao
(3)输入文件内容:
com.star.springboot.spi.DemoMysqlDao
com.star.springboot.spi.DemoOracleDao
3.测试
public class JdkSpiApplication {
public static void main(String[] args) {
ServiceLoader<DemoDao> serviceLoader = ServiceLoader.load(DemoDao.class);
serviceLoader.iterator().forEachRemaining(dao -> {
System.out.println(dao);
});
}
}
输出结果:
com.star.springboot.spi.DemoMysqlDao@65b3120a
com.star.springboot.spi.DemoOracleDao@6f539caf
控制台成功打印出DemoDao的两个实现类对象,这说明JDK原生的SPI机制已成功使用。
2.4.2 SpringFramework 3.2 的SPI
SpringFramework中的SPI比JDK原生的SPI更高级实用,因为它不仅限于接口或抽象类,而可以是任何一个类、接口或注解。
SpringBoot中大量用到SPI机制加载自动配置类和特殊组件等(如@EnableAutoCon