0%

JAVA_SPI机制

简介

在编写SpringBoot stater时我们会在项目的resources目录下新建META-INF文件夹并且在该目录下新建spring.factories文件,该文件中配置了在SpringBoot开启时需要加载和配置的类,那为什么只要配置了这个文件Spring就能自动去加载这个类呢?这个是因为用到了java的SPI机制

SPI

想象一下现在有两个项目A,B。他们之间存在依赖关系B依赖于项目A,也就是说B在A项目的上层,通常反映在我们需要在B的maven POM.xml文件中配置了A的依赖。那么现在处在下层的A工厂有一个抽象接口需要由下层应用去实现,(举个例子A工程是快递公司,B工程是一个电商客户,那么快递公司只要留一个统一的收快递的电话给电商客户,由电商客户去打电话叫快递公司去寄快递即可,对于快递公司它并不关心电商公司是寄送什么货物,快递公司只负责寄送即可,那么具体寄送什么货物的实现就由电商公司自行去实现),想象一下这个系统你会怎么写?我们只要实现快递公司提供的寄送货物的接口,然后再由电商公司调用寄送货物的接口即可,简单暴力完成解耦。那么现在有一个问题,由于快递公司和这家电商公司关系恶化,那么快递公司想把这家公司开除,然后和另外一家快递公司合作,那么我们要怎么做?是不是要修改代码,去掉这家被开除的电商公司的实现,然后加入新的电商公司的实现?有没有一个方法我们不需要修改代码只要修改一下配置文件即可?于是这里就延伸除了SPI机制,其实控制反转的实现依赖注入就是一个变相的SPI实现,我们只要配置Spring的xml文件就能完成替换我们不同的类的实现,这样我们就不要更改我们核心的代码,只要修改我们的配置类即可,这样大大降低了出错的可能和完成了解耦

JDK自带 SPI简单的代码实现

定义一个接口

我们定义一个父类汽车,他有一个方法获取汽车名

1
2
3
4
5
6
7
8
9
10
11
12
package com.liu;

/**
* @author Liush
* @description
* @date 2019/12/27 10:36
**/
public interface CarI {

String getName();

}
定义2个实现类分别为奔驰和宝马
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.liu;

/**
* @author Liush
* @description
* @date 2019/12/27 10:43
**/
public class BenZ implements CarI {

@Override
public String getName() {
return "我是奔驰";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.liu;

/**
* @author Liush
* @description
* @date 2019/12/27 10:37
**/
public class BMW implements CarI {
@Override
public String getName() {
return "我是宝马";
}
}
新建配置文件

因为我使用的是maven工程所以我在resources目录下新建services目录注意services目录时固定的,这个在jdk的代码中只识别这个目录,在services目录下新建配置文件,文件名为父接口的全路径名称,比如这里是com.liu.CarI,然后再文件中添加我们想要注入的实现类这里配置如下,我们将宝马和奔驰都注入

1
2
com.liu.BMW
com.liu.BenZ

调用

新建main函数,使用ServiceLoader去调用方法,我们会发现系统输入了我是宝马和我是奔驰,那么一个简单的SPI实现完成,这样就完成了代码的配置和代码的实现解耦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.liu;

import java.sql.Driver;
import java.util.Iterator;
import java.util.ServiceLoader;

/**
* @author Liush
* @description
* @date 2019/12/27 10:39
**/
public class MainTest {


public static void main(String[] args) {
ServiceLoader<CarI> serviceLoader=ServiceLoader.load(CarI.class);
Iterator<CarI> carIterator=serviceLoader.iterator();
while (carIterator.hasNext()){

System.out.println(carIterator.next().getName());
}


}

}