今天这篇文章就来深入聊一下SPI。
创新互联-专业网站定制、快速模板网站建设、高性价比西畴网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式西畴网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖西畴地区。费用合理售后完善,十载实体公司更值得信赖。
SPI 全称:Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的接口,它可以用来启用框架扩展和替换组件。
面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。
为了实现在模块装配的时候不用在程序里动态指明,这就需要一种服务发现机制。java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。这有点类似IOC的思想,将装配的控制权移到了程序之外。
这是一种JDK内置的一种服务发现的机制,用于制定一些规范,实际实现方式交给不同的服务厂商。如下图:
解耦、可拔插、面向接口编程、动态类加载。
当服务的提供者提供了一种接口的实现之后,需要在classpath下的 META-INF/services/ 目录里创建一个以服务接口命名的文件,这个文件里的内容就是这个接口的具体的实现类。
当其他的程序需要这个服务的时候,就可以通过查找这个jar包(一般都是以jar包做依赖)的META-INF/services/中的配置文件,配置文件中有接口的具体实现类名,可以根据这个类名进行加载实例化,就可以使用该服务了。JDK中查找服务的实现的工具类是:java.util.ServiceLoader。
不能按需加载,需要遍历所有的实现,并实例化,然后在循环中才能找到我们需要的实现。如果不想用某些实现类,或者某些类实例化很耗时,它也被载入并实例化了,这就造成了浪费。
获取某个实现类的方式不够灵活,只能通过 Iterator 形式获取,不能根据某个参数来获取对应的实现类。(Spring 的BeanFactory,ApplicationContext 就要高级一些了。)
多个并发多线程使用 ServiceLoader 类的实例是不安全的。
API是调用并用于实现目标的类、接口、方法等的描述;
SPI是扩展和实现以实现目标的类、接口、方法等的描述;
换句话说,API 为操作提供特定的类、方法,SPI 通过操作来符合特定的类、方法。
下面来一个简单的案例实现:比如每个动物都有不同的叫声,作为声纹系统会定义一个接口,如下:
public interface AnimalSay {
void say();
}
在这个系统中并没有实现具体的实现,但是在处理业务逻辑时有需要用到该实例,此时就需要用到SPI去加载实现类,定义一个AnimalManagerLoader,实现如下:
@Data
public class AnimalManagerLoader {
private static final AnimalManagerLoader INSTANCE = new AnimalManagerLoader();
private final ListanimalSays;
private AnimalManagerLoader() {
animalSays = load();
}
/**
* 通过SPI加载实现类
*/
private Listload() {
ArrayListanimalSays = new ArrayList<>();
Iteratoriterator = ServiceLoader.load(AnimalSay.class).iterator();
while (iterator.hasNext()){
animalSays.add(iterator.next());
}
return animalSays;
}
public static AnimalManagerLoader getInstance() {
return INSTANCE;
}
}
此时就可以通过AnimalManagerLoader中的load方法去加载对应的实现类,封装到List集合中,调用如下:
public static void main(String[] args) {
AnimalManagerLoader animalManagerLoader = AnimalManagerLoader.getInstance();
ListanimalSays = animalManagerLoader.getAnimalSays();
for (AnimalSay animalSay : animalSays) {
animalSay.say();
}
}
那么此时提供声音的厂家就需要实现这个接口,比如狗狗的声纹厂家,实现如下:
/**
* 狗狗的声纹
*/
public class DogSay implements AnimalSay {
public void say() {
System.out.println("wang wang ~");
}
}
猫咪的声纹如下:
/**
* 猫咪的声纹
*/
public class CatSay implements AnimalSay {
@Override
public void say() {
System.out.println("miao miao ~");
}
}
实现类定义了,就需要在 /META-INF/services 中定义一个 com.myjszl.animal.api.AnimalSay文件,内容如下:
com.myjszl.dog.api.DogSay
com.myjszl.dog.api.CatSay
SPI扩展机制应用场景有很多,比如Common-Logging,JDBC,Dubbo、ShardingSphere等等。
java中定义的java.sql.Driver接口,并没有具体的实现,实现方式而是交给不同的服务厂商:
在ShardingSphere中为了实现分布式事务提供了一个接口ShardingTransactionManager,但是在其架构中并未对其做出具体的实现,而是交给不同的厂商去实现,比如JTA强一致性事务的XAShardingTransactionManager,在其中META-INF/services就有一个org.apache.shardingsphere.transaction.spi.ShardingTransactionManager文件,如下图:
以上只是简单的列举了几个场景,实际应用场景很多,比如Spring、Spring Boot 中都有用到SPI设计。
Spring中大量使用了SPI;比如:对servlet3.0规范对ServletContainerInitializer的实现、自动类型转换Type Conversion SPI(Converter SPI、Formatter SPI)等
SLF4J加载不同提供商的日志实现类,比如log4j、log4j2、logback.....
通过Java的SPI机制能够很方便的实现可插拔、解耦的功能设计,在日常的开发中要能想到该机制并能灵活的运用。
文章题目:聊聊JavaSPI机制,你会了吗?
网页路径:http://www.shufengxianlan.com/qtweb/news49/531399.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联