今天讲讲我在看框架源码过程中遇到的关于组合模式的设计。
后续开个系列讲讲我阅读源码过程印象比较深的设计模式的使用(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
当进行令牌授予时,使用一个树枝组件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.总结:
这就是我读源码过程中,印象比较深的几处组合模式的应用。
理解这些优秀框架使用此模式时的场景,活学活用,当有类似业务时,可以尝试组合模式的使用。