0、前言
最近在学习Spring相关知识,突然就想着扒一扒在Spring中用到的设计模式,记录一下,与君共勉。
1、在Spring源码中的实现
a、策略注册
springMVC中初始化各种解析类
org.springframework.web.servlet.DispatcherServlet#initStrategies
这里初始化处理器适配器,本次扒的就是这块
org.springframework.web.servlet.DispatcherServlet#initHandlerAdapters
注:这里注意 this.handlerAdapters ,这个变量就是用来存储从Spring容器中扫描实现接口HandlerAdapter.class的所有Bean对象,用List存储方便后续使用。
b、策略使用
看看HandlerAdapter.class接口
org.springframework.web.servlet.HandlerAdapter
在策略适配器中,使用到的方法是 supports和handle。
supports用来匹配适配条件,handle用来执行具体的实现逻辑。
Spring中的使用
org.springframework.web.servlet.DispatcherServlet#getHandlerAdapter
spring中的使用就是很简单粗暴的循环List,遍历调用supports方法,返回结果为true则匹配成功,就将对应的HandlerAdapter的实现类返回进行调用处理。
2、照猫画虎
这里以比较常用的支付方式来进行模仿
a、定义
先定义一个接口 IPayService
/**
* 支付服务
* @author yearOfTheRain
* @date 2024/6/16 12:10
*/
public interface IPayService {
/**
* 支持方法
* @param payType 支付类型枚举
* @return
*/
boolean supports(PayTypeEnum payType);
/**
* 实际支付方法
* @param orderId 订单ID
* @param money 支付金额
*/
void doPay(String orderId, String money);
}
枚举类定义如下:
/**
* 支付类型枚举
* @author yearOfTheRain
* @date 2024/6/16 12:11
*/
public enum PayTypeEnum {
BALANCE("余额支付"),
WECHAT("微信支付"),
ALIPAY("支付宝支付"),
;
private String name;
PayTypeEnum(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
分别实现三种不同的支付方式的具体实现
/**
* 余额支付服务具体实现
* @author yearOfTheRain
* @date 2024/6/16 12:22
*/
@Component
public class BaLancePayService implements IPayService {
@Override
public boolean supports(PayTypeEnum payType) {
return PayTypeEnum.BALANCE.equals(payType);
}
@Override
public void doPay(String orderId, String money) {
System.out.println("执行余额支付逻辑");
}
}
@Component
public class WechatPayService implements IPayService {
@Override
public boolean supports(PayTypeEnum payType) {
return PayTypeEnum.WECHAT.equals(payType);
}
@Override
public void doPay(String orderId, String money) {
System.out.println("执行微信支付逻辑");
}
}
@Component
public class AliPayService implements IPayService {
@Override
public boolean supports(PayTypeEnum payType) {
return PayTypeEnum.ALIPAY.equals(payType);
}
@Override
public void doPay(String orderId, String money) {
System.out.println("执行支付宝支付逻辑");
}
}
b、策略工厂
定义一个工厂类,用来调度产出对应的支付服务实现类
/**
* 支付方式服务调度工厂
* @author yearOfTheRain
* @date 2024/6/16 12:29
*/
@Component
public class PayServiceFactory {
@Autowired
private List<IPayService> payServiceList;
/**
* 获取具体支付服务实现类
* @param payType
* @return
*/
public IPayService getPayService(PayTypeEnum payType) {
if (this.payServiceList != null) {
for (IPayService payService : this.payServiceList) {
if (payService.supports(payType)) {
return payService;
}
}
}
throw new RuntimeException("No adapter for handler [" + payServiceList +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
}
c、测试
我这里就简单的用枚举类遍历来调用下方法,可以看到对应的逻辑都有执行到。
3、变种使用
大家有没有发现上面的这种执行逻辑,每次都需要进行遍历循环。那么有没有什么方式可以去掉这层遍历呢?
接口调整
添加一个返回支持枚举类的方法
实现对应的方法
改造后的策略工厂
/**
* 支付方式服务调度工厂
* @author yearOfTheRain
* @date 2024/6/16 12:29
*/
@Component
public class PayServiceFactory implements ApplicationContextAware {
// @Autowired
// private List<IPayService> payServiceList;
private final EnumMap<PayTypeEnum, IPayService> payServiceEnumMap = new EnumMap<>(PayTypeEnum.class);
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, IPayService> beansOfType = applicationContext.getBeansOfType(IPayService.class);
for (IPayService value : beansOfType.values()) {
payServiceEnumMap.put(value.payType(), value);
}
}
// /**
// * 获取具体支付服务实现类
// * @param payType
// * @return
// */
// public IPayService getPayService(PayTypeEnum payType) {
// if (this.payServiceList != null) {
// for (IPayService payService : this.payServiceList) {
// if (payService.supports(payType)) {
// return payService;
// }
// }
// }
// throw new RuntimeException("No adapter for handler [" + payServiceList +
// "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
// }
public IPayService getPayService(PayTypeEnum payTypeEnum) {
IPayService payService = payServiceEnumMap.get(payTypeEnum);
if( payService == null) {
throw new RuntimeException("No adapter for handler [" + payTypeEnum +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
return payService;
}
}
还是同样的测试代码,运行后也是一样的结果。
@Autowired
private PayServiceFactory payServiceFactory;
@Test
void testPayService() {
for (PayTypeEnum payTypeEnum : PayTypeEnum.values()) {
payServiceFactory.getPayService(payTypeEnum).doPay("id","34.90");
}
}
4、总结
Spring中使用的supports判断方式可用于更复杂的判断条件,也可用多种条件来控制调度结果,会更加的灵活。
变种方式采用EunmMap进行缓存处理,缓存的时机是在Spring容器初始化后(因为实现了ApplicationContextAware)。目前只能进行简单的条件判断,换来的就是少了一层循环的判断。