共计 2601 个字符,预计需要花费 7 分钟才能阅读完成。
download:极客专栏打包 - 实时更新含文档源码
深入剖析 Spring Boot 的 SPI 机制
简介
SPI(Service Provider Interface) 是 JDK 内置的一种服务提供发现机制,可能用来启用框架扩大和替换组件, 次要用于框架中开发,例如 Dubbo、Spring、Common-Logging,JDBC 等采纳采纳 SPI 机制,针对同一接口采纳不同的实现提供应不同的用户,从而提高了框架的扩展性。
Java SPI 实现
Java 内置的 SPI 通过 java.util.ServiceLoader 类解析 classPath 和 jar 包的 META-INF/services/ 目录 下的以接口全限定名命名的文件,并加载该文件中指定的接口实现类,以此实现调用。
示例说明
创建动静接口
public interface VedioSPI
{
void call();
}
复制代码
实现类 1
public class Mp3Vedio implements VedioSPI
{
@Override
public void call()
{System.out.println("this is mp3 call");
}
}
复制代码
实现类 2
public class Mp4Vedio implements VedioSPI
{
@Override
public void call()
{System.out.println("this is mp4 call");
}
}
复制代码
在我的项目的 source 目录下新建 META-INF/services/ 目录下,创建 com.skywares.fw.juc.spi.VedioSPI 文件。
相干测试
public class VedioSPITest
{
public static void main(String[] args)
{ServiceLoader<VedioSPI> serviceLoader =ServiceLoader.load(VedioSPI.class);
serviceLoader.forEach(t->{t.call();
});
}
}
复制代码
说明:Java 实现 spi 是通过 ServiceLoader 来查找服务提供的工具类。
源码分析
上述只是通过简略的示例来实现下 java 的内置的 SPI 功能。其实现原理是 ServiceLoader 是 Java 内置的用于查找服务提供接口的工具类,通过调用 load()方法实现对服务提供接口的查找,最初遍从来逐个拜访服务提供接口的实现类。
从源码可能发现:
ServiceLoader 类本身实现了 Iterable 接口并实现了其中的 iterator 方法,iterator 方法的实现中调用了 LazyIterator 这个外部类中的方法,解析完服务提供接口文件后最终后果放在了 Iterator 中返回,并不反对服务提供接口实现类的间接拜访。
所有服务提供接口的对应文件都是搁置在 META-INF/services/ 目录下,final 类型决定了 PREFIX 目录不可变更。
诚然 java 提供的 SPI 机制的思维非常好,然而也存在相应的弊病。具体如下:
Java 内置的方法形式只能通过遍从来获取
服务提供接口必须放到 META-INF/services/ 目录下。
针对 java 的 spi 存在的问题,Spring 的 SPI 机制沿用的 SPI 的思维,但对其进行扩大和优化。
Spring SPI
Spring SPI 沿用了 Java SPI 的设计思维,Spring 采纳的是 spring.factories 形式实现 SPI 机制,可能在不修改 Spring 源码的前提下,提供 Spring 框架的扩展性。
Spring 示例
定义接口
public interface DataBaseSPI
{
void getConnection();
}
复制代码
相干实现
DB2 实现
public class DB2DataBase implements DataBaseSPI
{
@Override
public void getConnection()
{System.out.println("this database is db2");
}
}
Mysql 实现
public class MysqlDataBase implements DataBaseSPI
{
@Override
public void getConnection()
{System.out.println("this is mysql database");
}
}
复制代码
1. 在我的项目的 META-INF 目录下,新增 spring.factories 文件
2. 填写相干的接口信息,内容如下:
com.skywares.fw.juc.springspi.DataBaseSPI = com.skywares.fw.juc.springspi.DB2DataBase, com.skywares.fw.juc.springspi.MysqlDataBase
复制代码
说明多个实现采纳逗号分隔。
相干测试类
public class SpringSPITest
{
public static void main(String[] args)
{
List<DataBaseSPI> dataBaseSPIs =SpringFactoriesLoader.loadFactories(DataBaseSPI.class,
Thread.currentThread().getContextClassLoader());
for(DataBaseSPI datBaseSPI:dataBaseSPIs){datBaseSPI.getConnection();
}
}
}
从示例中咱们看出,Spring 采纳 spring.factories 实现 SPI 与 java 实现 SPI 非常相似,然而 spring 的 spi 形式针对 java 的 spi 进行的相干优化具体内容如下:
Java SPI 是一个服务提供接口对应一个配置文件,配置文件中存放以后接口的所有实现类,多个服务提供接口对应多个配置文件,所有配置都在 services 目录下;
Spring factories SPI 是一个 spring.factories 配置文件存放多个接口及对应的实现类,以接口全限定名作为 key,实现类作为 value 来配置,多个实现类用逗号隔开,仅 spring.factories 一个配置文件。
总结
本文粗疏的讲解了 java 和 Spring 的 SPI 机制,SPI 技术将服务接口与服务实现进行分离实现解耦,从而晋升程序的可扩展性。