策略设计模式

本文介绍了策略设计模式的使用,通过模拟Spring MVC参数解析,展示了如何管理和扩展参数解析器。在Spring源码中,参数解析器的实现应用了策略模式,允许通过实现特定接口并添加到容器列表中进行扩展。文章还探讨了Spring自带参数解析器的添加方式,并讨论了策略模式的优缺点。

策略设计模式在spring源码中的应用

策略设计模式的使用

我就结合着我自己的学习,来讲下,我对策略模式的一些理解,首先说,使用背景,简单来说,就是有多种策略的场景下使用,这个说的好像是个废话,举个简单的例子吧

我们就模拟下springmvc源码中,对请求参数的解析逻辑

参数解析接口
public interface ParamResolve {

    /**
     * 判断当前解析类是否可以解析param该参数,返回true表示支持,返回false表示不支持
     * @param param
     * @return
     */
    boolean isSupportResolve(Object param);

    /**
     * 解析参数
     * @param param
     */
    void resolveParam(Object param);

}
参数解析实现类
public class JsonResolve implements ParamResolve{

    @Override
    public boolean isSupportResolve(Object param) {
        return "Json".equals(param);
    }

    @Override
    public void resolveParam(Object param) {
        System.out.println("解析json参数");
    }
}
public class ListResolve implements ParamResolve {

    @Override
    public boolean isSupportResolve(Object param) {
        return "List".equals(param);
    }

    @Override
    public void resolveParam(Object param) {
        System.out.println("解析list参数");
    }
}

public class StringResolve implements ParamResolve{

    @Override
    public boolean isSupportResolve(Object param) {
        return "String".equals(param);
    }

    @Override
    public void resolveParam(Object param) {
        System.out.println("对String类型的参数进行解析");
    }
}

这里我模拟了三个参数解析器,分别是对String参数的解析,json参数的解析、list类型参数的解析,只是简单的模拟下demo

我们需要考虑,虽然我提供了三个参数解析器,但是我怎么知道哪个参数是要被哪个解析器去解析呢?那这时候就需要一个额外的类,去管理所有的参数解析器

public class Context {

    public static void main(String[] args) {
        List<ParamResolve> paramResolves = new ArrayList<>();
        paramResolves.add(new ListResolve());
        paramResolves.add(new StringResolve());
        paramResolves.add(new JsonResolve());

        test(paramResolves,"String");
        test(paramResolves,"Json");
        test(paramResolves,"List");

    }

    public static void test(List<ParamResolve> paramResolves,String str){
        for (ParamResolve paramResolve: paramResolves) {
            if(paramResolve.isSupportResolve(str)){
                paramResolve.resolveParam(str);
            }
        }
    }
}

这就是参数管理所有参数解析器的类,也就是说,如果这时候入参的是String类型的参数,我会先调用isSupportResolve方法,如果这个方法返回的是true,就表示当前解析器就可以解析这个参数,那接着就会调用它的解析方法去解析这个参数

那如果我这时候,需要去扩展第四个参数解析器怎么办?因为我还可能提供对Map类型参数的解析、对Integer类型参数的解析?那只需要去实现ParamResolve接口,然后将实现类,放到paramResolves这个list集合中即可

我觉得这就是策略设计模式的应用

在spring源码中的应用

那其实springmvc源码中,参数解析器就是策略模式的应用,如果看过springmvc参数解析源码的同学,就会发现,我上面写得demo,和springmvc参数解析的逻辑神似,

参数解析器接口
可以看到,在这个接口中,只定义了两个方法,分别是是否支持该参数的解析,第二个是参数解析的方法
org.springframework.web.method.support.HandlerMethodArgumentResolver
public interface HandlerMethodArgumentResolver {
    boolean supportsParameter(MethodParameter var1);

    Object resolveArgument(MethodParameter var1, ModelAndViewContainer var2, NativeWebRequest var3, WebDataBinderFactory var4) throws Exception;
}

在这里插入图片描述

可以看到,spring自己本身就提供了N多个参数解析器
那这时候问题就来了,这些spring自带的参数解析器是在什么时候添加到spring源码中的list集合中的?这个问题最后说吧

那对于spring的源码中,我们如何将自定义的参数解析器,添加到spring容器中的呢?
在spring源码中,也是用的一个list集合来存放所有的参数解析器,所以,只需要将我们自己定义的参数解析器,放入到这个list集合中即可

org.springframework.web.method.support.HandlerMethodArgumentResolverComposite#argumentResolvers 
private final List<HandlerMethodArgumentResolver> argumentResolvers =
			new LinkedList<HandlerMethodArgumentResolver>();

那怎么放到这个list中呢?spring也提供了扩展点,只需要实现WebMvcConfigurer这个接口,然后实现它的addArgumentResolvers()方法即可,具体,可以参考前面我的一篇笔记 springmvc应用-自定义参数解析器

那我们放入到了集合之后,在发起调用的时候,spring会遍历这个list集合,然后对请求的中的参数,依次调用
伪代码大致就是这样的:

for(HandlerMethodArgumentResolver 参数解析器:参数解析器list集合){
	for(Param param:params){
		if(参数解析器.isSupport(param)){
			参数解析器.解析参数
		}
	}
}

spring源码中,当然不会蠢到这样去处理,在其内部它会用一个map集合,存下来参数所对应的解析器,这样在重复调用的时候,就不需要再次解析了,这里只是伪代码的实现

所以,这就是策略设计模式的使用和在spring源码中的应用
我觉得spring的强大之处就在于:自己本身已经足够强大了,如果自己提供的一些功能还是满足不了程序员的业务需求,那怎么办呢?没关系,我spring提供了一系列的扩展点,足够来让程序员去扩展,简直是一条龙服务

spring自带的参数解析器,如何放到list集合中

最简单的一个办法,我们在所有addResolve的地方,debug打上断点,然后看下,是在哪里调用的,其实是在RequestMappingHandlerAdapter这个bean初始化的时候,将所有默认的参数解析器,添加到list集合中的,
在这里插入图片描述
可以看到,它实现了初始化回调的接口,所以,在afterPropertiesSet()方法中,提供了addResolve的逻辑

public void afterPropertiesSet() {
    this.initControllerAdviceCache();
    List handlers;
    if (this.argumentResolvers == null) {
        handlers = this.getDefaultArgumentResolvers();
        this.argumentResolvers = (new HandlerMethodArgumentResolverComposite()).addResolvers(handlers);
    }

    if (this.initBinderArgumentResolvers == null) {
        handlers = this.getDefaultInitBinderArgumentResolvers();
        this.initBinderArgumentResolvers = (new HandlerMethodArgumentResolverComposite()).addResolvers(handlers);
    }

    if (this.returnValueHandlers == null) {
        handlers = this.getDefaultReturnValueHandlers();
        this.returnValueHandlers = (new HandlerMethodReturnValueHandlerComposite()).addHandlers(handlers);
    }

}


在这里插入图片描述

所以,这就是默认的参数解析器,是如何放到list的答案

优缺点

策略设计模式的优点:

策略设计模式是对开闭原则的完美诠释,如果我要新增一个策略,只需要添加新的策略即可,不会对原有的算法和策略产生影响

缺点:
使用者需要知道每个策略的具体实现,来决定使用哪个策略

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值