从Spring中扒设计模式之策略适配器

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)。目前只能进行简单的条件判断,换来的就是少了一层循环的判断。

  • 26
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值