标签:机制 策略 相关 web项目 clip err interface orm 消费者
目录
关于SPI的定义参考博客:https://blog.csdn.net/gallenzhang/article/details/88958800
SPI是java内置的一种服务发现机制,一般在框架设计的时候,将问题抽象成接口,至于服务的实现,由不同的厂家来各自实现,按照指定的规范引入哪个厂家的实现jar包,就可以使用该厂家的服务实现,这种现象个人理解跟java的多态很类似。
假设现在没有使用SPI,存在一个接口,和不同的实现类,然后在使用该接口的时候,需要硬编码的形式使用哪个实现类,例如使用工厂模式+策略模式,根据指定条件硬编码,如果我们新增加一种实现,还是需要修改工厂模式的编码,所以跟业务代码存在耦合。而使用SPI机制,可以将第三方的实现方式作为插件的方式,可插拔方式,可以很大程度的与业务代码进行解耦。
但是,这种方式也有缺点,假设配置了多个实现类,不能很好的根据条件的来进行判断筛选,只能针对Iterator遍历获取。
如果从代码接入的级别来看,策略模式还是在原有项目中进行代码修改,只不过它不会修改原有类中的代码,而是新建了一个类。而 SPI 机制则是不会修改原有项目中的代码,其会新建一个项目,最终以 Jar 包引入的方式代码。
从这一点来看,无论策略模式还是 SPI 机制,他们都是将修改与原来的代码隔离开来,从而避免新增代码对原有代码的影响。但策略模式是类层次上的隔离,而 SPI 机制则是项目框架级别的隔离。
从应用领域来说,策略模式更多应用在业务领域,即业务代码书写以及业务代码重构。而 SPI 机制更多则是用于框架的设计领域,通过 SPI 机制提供的灵活性,让框架拥有良好的插件特性,便于扩展。
在这个demo中,不管是服务提供者还是消费者,以及调用者都是在不同的工程中,不要全部都放在一个工程里,这样比较符合规范和设计,以及能够比较清楚的理解其中的使用细节。
由于是demo,这里就提供一个计算接口,由不同的实现来实现数据的计算,输出结果。
这里创建一个工程 spi-interface,里面专门提供服务接口。
package services;
/**
* @author ztkj-hzb
* @Date 2020/1/12 14:00
* @Description
*/
public interface ICalculationService {
/**
* 数学计算
*
* @param param1
* @param param2
* @return
*/
Object calc(Object param1, Object param2);
}
项目结构如下图:
如果不会使用idea或者eclipse打包的,可以百度一下,这里提供一下关于idea打包的参考博客:https://www.cnblogs.com/flyToDreamJava/p/8991201.html
新建一个工程 spi-impl1,在这里实现了计算接口,使用加法进行数据的运算。注意:这里还需要引入刚才服务提供者的打包好的jar包,不然找不到需要实现的接口。
package service.impl;
import services.ICalculationService;
/**
* @author ztkj-hzb
* @Date 2020/1/12 14:06
* @Description
*/
public class CalculationServiceImplB implements ICalculationService {
/**
* 加法运算
* @param o
* @param o1
* @return
*/
@Override
public Object calc(Object o, Object o1) {
return (Double)o + (Double)o1;
}
}
创建目录 META-INF/services,从上述的服务提供者结构图来看,我们得知接口的全类名是 "services.ICalculationService" , 所以我们需要创建一个文件,名字就是我们的全类名,里面的内容,就是我们刚才的实现类的全类名。
具体的项目结构如下图:
服务消费者B项目结构跟A几乎一致,本不想重复写,考虑有些朋友跟我一样是新手,所以就多写一点。
新建一个工程 spi-impl2,在这里实现了计算接口,使用减法进行数据的运算。再次提醒注意:这里还需要引入刚才服务提供者的打包好的jar包,不然找不到需要实现的接口。
package service.impl;
import services.ICalculationService;
/**
* @author ztkj-hzb
* @Date 2020/1/12 14:08
* @Description
*/
public class CalculationServiceImplA implements ICalculationService {
/**
* 减法运算
*
* @param o
* @param o1
* @return
*/
@Override
public Object calc(Object o, Object o1) {
return (Double) o - (Double) o1;
}
}
创建目录 META-INF/services,从上述的服务提供者结构图来看,我们得知接口的全类名是 "services.ICalculationService" , 所以我们需要创建一个文件,名字就是我们的全类名,里面的内容,就是我们刚才的实现类的全类名。
具体的项目结构如下图:
创建一个工程 spi-main, 注意:需要引用上面三个项目的jar包。
package com.lonely;
import services.ICalculationService;
import java.util.Iterator;
import java.util.ServiceLoader;
/**
* @author ztkj-hzb
* @Date 2020/1/12 14:18
* @Description
*/
public class Main {
public static void main(String[] args) {
ServiceLoader<ICalculationService> serviceLoader = ServiceLoader.load(ICalculationService.class);
Iterator<ICalculationService> iterator = serviceLoader.iterator();
while(iterator.hasNext()){
ICalculationService calculationService = iterator.next();
System.out.println(MessageFormat.format("指定类:{0}.calc()执行结果是:{1}",calculationService.getClass().getName(),calculationService.calc(23.1,15.3)));
}
}
}
执行结果如下:
指定类:service.impl.CalculationServiceImplA.calc()执行结果是:7.8
指定类:service.impl.CalculationServiceImplB.calc()执行结果是:38.4
从结果上看,我们在调用方工程代码中,没有写上一行关于 "CalculationServiceImplA"或者"CalculationServiceImplB"相关服务的硬代码,只要后续新添加一种实现,只需要在编写一个工程,按照SPI约定,打包成jar后,放入到调用者工程中,就能使用了,完全与具体实现解耦。
标签:机制 策略 相关 web项目 clip err interface orm 消费者
原文地址:https://www.cnblogs.com/duguxiaobiao/p/12183135.html