从框架源码学设计模式之组合模式(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.
    在这里插入图片描述
    以我们熟悉的@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
图1

当进行令牌授予时,使用一个树枝组件CompositeTokenGranter来统一对不同叶子组件(真正授权的TokenGranter)的调用
在这里插入图片描述
树枝和叶子实现统一实现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.总结:

这就是我读源码过程中,印象比较深的几处 组 合 模 式 \color{red}{组合模式} 的应用。

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


如果本文任何错误,请批评指教,不胜感激 !
如果文章哪些点不懂,可以联系我交流学习!

微信公众号:源码行动
享学源码,行动起来,来源码行动

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值