SPI
英文为 Service Provider Interface
,即服务提供者接口,SPI 将服务接口和具体的服务实现分离开来,将服务调用方和服务实现者解耦,能够提升程序的扩展性、可维护性。修改或者替换服务实现并不需要修改调用方。java很多优良的开源框架都使用了spi机制。话不多说,直接上实战代码。
场景介绍,公司开发了一套支付的中间服务,分支持支付宝支付、微信支付、银联支付,而公司的产品有微信公众号、支付宝生活号,在微信公众号中不需要支付宝支付,在支付宝生活号中不需要使用微信支付,由于在不同的客户端需要的支付方式不一样,那么在调用支付中间件的时候,怎么实现热插拔,按需调用支付是必须考虑的问题,而spi机制就是解决这种场景问题的不二之选。
1、创建支付服务的starter (pay-service-spring-boot-starter)项目
2、pom文件添加场景启动器依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
3、定义支付接口
public interface IPayService {
String pay(String goods,float fee);
}
4、支付实现
(1)支付宝支付
public class AliPayService implements IPayService {
@Override
public String pay(String goods, float fee) {
return new Date()+"[支付宝支付成功"+fee+",您已经成功购买"+goods+"]";
}
}
(2)微信支付
public class WechatPayService implements IPayService {
@Override
public String pay(String goods, float fee) {
return new Date()+"[微信支付成功"+fee+",您已经成功购买"+goods+"]";
}
}
(3)银联支付
public class UnionPayService implements IPayService {
@Override
public String pay(String goods, float fee) {
return new Date()+"[银联支付成功"+fee+",您已经成功购买"+goods+"]";
}
}
(4)支付异常
public class ErrorPayService implements IPayService {
@Override
public String pay(String goods, float fee) {
return "服务出现异常,无法正常支付";
}
}
(5)定义支付类型
public enum PayWayEnum {
/**
* 支付类型
*/
ALI("zfb"),
WECHAT("wx"),
UNION("union");
private String name;
PayWayEnum(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
5、支付服务自动配
@ConfigurationProperties(prefix = "spi.pay.way")
public class PayWayProperties {
/**
* 支付宝: zfb 微信:wx 银联:union
*/
private String payName;
}
@Configuration
@ConditionalOnMissingBean(IPayService.class)
@EnableConfigurationProperties(PayWayProperties.class)
public class PayServiceAutoConfigure {
@Bean
public IPayService payService(PayWayProperties payWayProperties){
ServiceLoader<IPayService> iPayServiceServiceLoader = ServiceLoader.load(IPayService.class);
Iterator<IPayService> iterator = iPayServiceServiceLoader.iterator();
IPayService payService=null;
String payName = payWayProperties.getPayName();
while (iterator.hasNext()){
payService = iterator.next();
if(payService instanceof AliPayService && PayWayEnum.ALI.getName().equals(payName)){
break;
}
if(payService instanceof WechatPayService && PayWayEnum.WECHAT.getName().equals(payName)){
break;
}
if(payService instanceof UnionPayService && PayWayEnum.UNION.getName().equals(payName)){
break;
}
}
if(payService==null){
payService =new ErrorPayService();
}
return payService;
}
}
6、配置spring.factories和spi文件
在resources目录下创建META-INF目录,在META-INF目录下创建services目录,目录文件结构如下:
spring.factories文件的内容:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.cn.fcw.service.config.PayServiceAutoConfigure
spi配置文件org.cn.fcw.service.IPayService(文件使用支付接口的全路径命名),内容为具体实现类的全路径
org.cn.fcw.service.impl.UnionPayService
org.cn.fcw.service.impl.AliPayService
org.cn.fcw.service.impl.WechatPayService
7、测试使用
创建测试过程
(1)pom加入依赖
<dependency>
<groupId>org.cn.fcw</groupId>
<artifactId>pay-service-spring-boot-starter</artifactId>
<version>1.0</version>
</dependency>
(2)application.yml加入配置,选择具体的支付方式
spi:
pay:
way:
#根据具体的需要选择 zfb 、wx 、 union
payName: zfb
(3)测试调用
@RunWith(SpringRunner.class)
@SpringBootTest
public class AppTest {
@Autowired
private IPayService payService;
@Test
public void payTest(){
String res = payService.pay("苹果", 56.6f);
System.out.println("res-------"+res);
}
}
输出结果: