从框架源码学设计模式之组合模式(1)

简介: 从框架源码学设计模式之组合模式(1)

今天讲讲我在看框架源码过程中遇到的关于组合模式的设计。

后续开个系列讲讲我阅读源码过程印象比较深的设计模式的使用(Spring,SpringMVC,Mybaits,Security,SpringCloud等等)。对于理解框架与业务代码的优化很有帮助


1.基本说明


组合模式往往分为三个角色:

  • Component抽象构件角色: 定义抽象行为或者属性
  • Leaf叶子组件: 可以理解为真正干活的。
  • Composite树枝构件:  树枝组件组合了leaf组件。内部维护一个leaf组件列表。

优点: 1、高层模块调用简单。 2、节点自由增加。

缺点:在使用组合模式时,其叶子和树枝的声明都是实现类,而不是接口,违反了依赖倒置原则。


2.SpringMVC框架中的组合模式:


HandlerAdapter 做为Servlet与Handler 多样性的适配器,用于处理参数映射,返回值适配。

以常用的常用的RequestMappingHandlerAdapter为例子,RequestMappingHandlerAdapter使用了组合模式处理多样化参数映射问题,返回值映射等问题

public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
        implements BeanFactoryAware, InitializingBean {
    //参数处理组合器
    private HandlerMethodArgumentResolverComposite argumentResolvers;
    //intBinder注解处理组合器
    private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;
    //返回值处理组合器
    private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
}

我们来看看参数处理组合器与返回值处理组合器,带你找找你熟悉的东西。


2.1参数映射组合模式的使用:
  • Component组件抽象行为:HandlerMethodArgumentResolver接口:定义参数解析行为。主要包括两个
public interface HandlerMethodArgumentResolver {
//是否支持当前形式的参数映射解析
boolean supportsParameter(MethodParameter parameter);
//解析参数
Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;
}
  • 叶子节点:真正干活的。spring为我们提供了不少具体的HandlerMethodArgumentResolver,当然我们也可以添加自定义的HandlerMethodArgumentResolver.


image.png


  • 以我们熟悉的@PathVariable为例,当我们定义这样接受参数时。
@RequestMapping(value = "/checkIsExist/{phone}")
    public Object checkIsExist(@PathVariable String phone) {
    。。。
    }

其实是由叶子组件PathVariableMapMethodArgumentResolver替我们把请求里对应的参数识别出来,设置到此接口。

public class PathVariableMapMethodArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        是否包含@PathVariable注解
        PathVariable ann = parameter.getParameterAnnotation(PathVariable.class);
        return (ann != null && (Map.class.isAssignableFrom(parameter.getParameterType()))
                && !StringUtils.hasText(ann.value()));
    }
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
    }
  • 树枝组件HandlerMethodArgumentResolverComposite ,此组件统一对参数这一解析行为的调用。(这也表明了优点1:高层模块调用简单)

》》》》》》处理过程

private HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite();
private Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {
        (1)
        if (this.argumentResolvers.supportsParameter(parameter)) {
                try {
                    (2)
                    args[i] = this.argumentResolvers.resolveArgument(
                            parameter, mavContainer, request, this.dataBinderFactory);
                    continue;
                }
                catch (Exception ex) {
                    if (logger.isDebugEnabled()) {
                        logger.debug(getArgumentResolutionErrorMessage("Error resolving argument", i), ex);
                    }
                    throw ex;
                }
            }
}

(1) 校验是否支持当前参数的解析,HandlerMethodArgumentResolverComposite组合器,遍历所有的叶子参数解析器,并调用其supportsParameter方法,寻找一个支持当前参数映射的叶子参数解析器,会找到PathVariableMapMethodArgumentResolver.把其缓存起来。

@RequestMapping(value = "/checkIsExist/{phone}")
    public Object checkIsExist(@PathVariable String phone) {
    。。。
    }

(2) 调用HandlerMethodArgumentResolverComposite组合器resolveArgument方法,其方法内会调用PathVariableMapMethodArgumentResolver的resolveArgument进行真正的参数映射


2.2返回值映射组合模式的使用:

类似的返回值也是一样的做法:

  • 抽象组件HandlerMethodReturnValueHandler定义行为方法
  • 叶子组件: 除了spring为我们提供的组件外,我们也可以自定义。列如:我们会在接口方法上加上RequestBody注解直接返回非视图结果,其实就是由RequestResponseBodyMethodProcessor(叶子组件)返回值解析器做的
  • 树枝组件HandlerMethodReturnValueHandlerComposite

》》》》》》处理过程

  • 调用混合器HandlerMethodReturnValueHandlerComposite的handleReturnValue方法
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
this.returnValueHandlers.handleReturnValue(
                    returnValue, getReturnValueType(returnValue), mavContainer, webRequest);

调用混合器HandlerMethodReturnValueHandlerComposite.handleReturnValue方法内部会遍历找到支持当前返回类型的HandlerMethodReturnValueHandler(叶子组件),并调用其handleReturnValue方法进行处理

public void handleReturnValue(Object returnValue, MethodParameter returnType,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
        if (handler == null) {
            throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
        }
        handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
    }


3.Security Oauth2框架的组合模式:


TokenGranter令牌授予者,不同的认证模式使用TokenGranter

image.png


当进行令牌授予时,使用一个树枝组件CompositeTokenGranter来统一对不同叶子组件(真正授权的TokenGranter)的调用


image.png


树枝和叶子实现统一实现TokenGranter 接口,树枝内部组合实际工作的叶子

public interface TokenGranter {
    OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest);
}

调用授权方法时,其实调用的树枝组件CompositeTokenGranter的grant方法

//getTokenGranter()获取的是CompositeTokenGranter
OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);

树枝CompositeTokenGranter内部

public class CompositeTokenGranter implements TokenGranter {
    //不同模式授权组件的集合
    private final List<TokenGranter> tokenGranters;
    public CompositeTokenGranter(List<TokenGranter> tokenGranters) {
        this.tokenGranters = new ArrayList<TokenGranter>(tokenGranters);
    }
    //这里循环遍历所有的令牌授予者,且只有一个会返回token。
    public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
        for (TokenGranter granter : tokenGranters) {
            OAuth2AccessToken grant = granter.grant(grantType, tokenRequest);
            if (grant!=null) {
                return grant;
            }
        }
        return null;
    }
    public void addTokenGranter(TokenGranter tokenGranter) {
        if (tokenGranter == null) {
            throw new IllegalArgumentException("Token granter is null");
        }
        tokenGranters.add(tokenGranter);
    }
}


4.总结:


这就是我读源码过程中,印象比较深的几处的应用。

理解这些优秀框架使用此模式时的场景,活学活用,当有类似业务时,可以尝试组合模式的使用。


相关文章
设计模式 存储 人工智能
142 0
|
4月前
|
设计模式 存储 缓存
Netty源码—9.性能优化和设计模式
本文主要介绍了Netty的两大性能优化工具、FastThreadLocal的源码和总结、Recycler的设计理念/使用/四个核心组件/初始化/对象获取/对象回收/异线程收割对象和总结,以及Netty设计模式的单例模式和策略模式。
165 53
|
5月前
|
设计模式 Java 定位技术
【设计模式】【结构型模式】组合模式(Composite)
一、入门 什么是组合模式 组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次关系。组合模式使得客户端可以统一处理单个对象和组合对
172 10
|
8月前
|
设计模式 存储 Java
「全网最细 + 实战源码案例」设计模式——责任链模式
责任链模式(Chain of Responsibility Pattern)是一种行为型设计模式,允许将请求沿着处理者链进行发送。每个处理者可以处理请求或将其传递给下一个处理者,从而实现解耦和灵活性。其结构包括抽象处理者(Handler)、具体处理者(ConcreteHandler)和客户端(Client)。适用于不同方式处理不同种类请求、按顺序执行多个处理者、以及运行时改变处理者及其顺序的场景。典型应用包括日志处理、Java Web过滤器、权限认证等。
174 13
「全网最细 + 实战源码案例」设计模式——责任链模式
|
8月前
|
设计模式 存储 算法
「全网最细 + 实战源码案例」设计模式——命令模式
命令模式(Command Pattern)是一种行为型设计模式,将请求封装成独立对象,从而解耦请求方与接收方。其核心结构包括:Command(命令接口)、ConcreteCommand(具体命令)、Receiver(接收者)和Invoker(调用者)。通过这种方式,命令的执行、撤销、排队等操作更易扩展和灵活。 适用场景: 1. 参数化对象以操作。 2. 操作放入队列或远程执行。 3. 实现回滚功能。 4. 解耦调用者与接收者。 优点: - 遵循单一职责和开闭原则。 - 支持命令组合和延迟执行。 - 可实现撤销、恢复功能。 缺点: - 增加复杂性和类数量。
260 14
「全网最细 + 实战源码案例」设计模式——命令模式
|
8月前
|
设计模式 算法 开发者
「全网最细 + 实战源码案例」设计模式——策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,用于定义一系列可替换的算法或行为,并将它们封装成独立的类。通过上下文持有策略对象,在运行时动态切换算法,提高代码的可维护性和扩展性。适用于需要动态切换算法、避免条件语句、经常扩展算法或保持算法独立性的场景。优点包括符合开闭原则、运行时切换算法、解耦上下文与策略实现、减少条件判断;缺点是增加类数量和策略切换成本。示例中通过定义抽象策略接口和具体策略类,结合上下文类实现动态算法选择。
253 8
「全网最细 + 实战源码案例」设计模式——策略模式
|
8月前
|
设计模式 SQL 算法
「全网最细 + 实战源码案例」设计模式——模板方法模式
模板方法模式是一种行为型设计模式,定义了算法的骨架并在父类中实现不变部分,将可变部分延迟到子类实现。通过这种方式,它避免了代码重复,提高了复用性和扩展性。具体步骤由抽象类定义,子类实现特定逻辑。适用于框架设计、工作流和相似算法结构的场景。优点包括代码复用和符合开闭原则,缺点是可能违反里氏替换原则且灵活性较低。
204 7
「全网最细 + 实战源码案例」设计模式——模板方法模式
|
8月前
|
设计模式 存储 安全
「全网最细 + 实战源码案例」设计模式——组合模式
组合模式(Composite Pattern)是一种结构型设计模式,用于将对象组合成树形结构以表示“部分-整体”的层次结构。它允许客户端以一致的方式对待单个对象和对象集合,简化了复杂结构的处理。组合模式包含三个主要组件:抽象组件(Component)、叶子节点(Leaf)和组合节点(Composite)。通过这种模式,客户端可以统一处理简单元素和复杂元素,而无需关心其内部结构。适用于需要实现树状对象结构或希望以相同方式处理简单和复杂元素的场景。优点包括支持树形结构、透明性和遵循开闭原则;缺点是可能引入不必要的复杂性和过度抽象。
228 22
|
8月前
|
设计模式 存储 缓存
「全网最细 + 实战源码案例」设计模式——享元模式
享元模式(Flyweight Pattern)是一种结构型设计模式,旨在减少大量相似对象的内存消耗。通过分离对象的内部状态(可共享、不变)和外部状态(依赖环境、变化),它有效减少了内存使用。适用于存在大量相似对象且需节省内存的场景。模式优点包括节省内存和提高性能,但会增加系统复杂性。实现时需将对象成员变量拆分为内在和外在状态,并通过工厂类管理享元对象。
278 92
|
9月前
|
设计模式 前端开发 数据库
「全网最细 + 实战源码案例」设计模式——桥接模式
桥接模式(Bridge Pattern)是一种结构型设计模式,通过将抽象部分与实现部分分离,使它们可以独立变化,从而降低代码耦合度,避免类爆炸,提高可扩展性。其结构包括实现类接口、具体实现类、抽象类和精确抽象类。适用于多维度扩展类、隐藏实现细节、简化庞杂类以及运行时切换实现方法的场景。优点包括高扩展性、隐藏实现细节、遵循开闭原则和单一职责原则;缺点是可能增加代码复杂度。示例中展示了不同操作系统播放不同格式视频文件的实现。
196 19