之前的博客中springmvc应用-自定义参数解析器,主要介绍了自定义参数解析器是如何被使用的,最近看了一下组合设计模式,想到好像在springmvc源码中有用到的这个设计模式,就翻了下代码,引出了这个疑问

前面博客中,有简单的说过,只要我们在代码中写一个WebMvconfigurer的继承类,复写addArgumentResolvers()方法,springmvc就会自动的将我们要添加的参数解析器添加到spring容器中,这篇博客,我想记录下是如何存入到spring容器中的

WebMvcConfigurationSupport#requestMappingHandlerAdapter

这里为什么要先说这个类?因为我认为这是入口
在WebMvcConfigurationSupport中,会通过@Bean的形式,初始化requestMappingHandlerAdapter这个类

这里的这一行代码是关键,这行代码一共执行了两个操作,两个操作还都非常重要

getArgumentResolvers()

protected final List<HandlerMethodArgumentResolver> getArgumentResolvers() {if (this.argumentResolvers == null) {this.argumentResolvers = new ArrayList<>();addArgumentResolvers(this.argumentResolvers);}return this.argumentResolvers;
}

这里的嵌套逻辑比较深,一点一点来看

@Override
protected void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {this.configurers.addArgumentResolvers(argumentResolvers);
}
☆

这里this.configurers.addArgumentResolvers(argumentResolvers);里面就用到了组合设计模式

这是org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration这个类,在下面这个代码中,
是将所有的WebMvcConfigurer的实现类,放入到configurers 中的一个集合中,WebMvcConfigurerComposite 和WebMvcConfigurer是什么关系?
前者是后者的实现类private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {if (!CollectionUtils.isEmpty(configurers)) {this.configurers.addWebMvcConfigurers(configurers);}
}
org.springframework.web.servlet.config.annotation.WebMvcConfigurerComposite
private final List<WebMvcConfigurer> delegates = new ArrayList<>();public void addWebMvcConfigurers(List<WebMvcConfigurer> configurers) {if (!CollectionUtils.isEmpty(configurers)) {this.delegates.addAll(configurers);}
}

结合着上面两部分代码,可以看到,spring将WebMvcConfigurer的实现类全部放入到了delegates 中,这个很重要,在后面有用到;为什么有用?因为我们要想把一个自定义参数解析器放到spring容器中,就必须自己定义一个WebMvcConfigurer的实现类

好了,我们接着从上面有标识☆的位置继续看

@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {for (WebMvcConfigurer delegate : this.delegates) {delegate.addArgumentResolvers(argumentResolvers);}
}

上面的this.configurers.addArgumentResolvers(argumentResolvers);这行代码,调用的就是上面这个代码逻辑,可以看到,就是遍历所有的delegates,为什么遍历?因为delegates这里面存储的都是WebMvcConfigurer的实现类,自然也会调用到我们自己写的实现类,然后调用我们覆写的addArgumentResolvers()方法

adapter.setCustomArgumentResolvers(getArgumentResolvers());

接着来看adapter.setCustomArgumentResolvers()这个操作,我们知道getArgumentResolvers()返回的是所有的参数解析器,这里有个特别需要说明的,getArgumentResolvers()返回的是所有我们自定义的,因为spring自带的参数解析器,并不是在这里返回的

public void setCustomArgumentResolvers(@Nullable List<HandlerMethodArgumentResolver> argumentResolvers) {this.customArgumentResolvers = argumentResolvers;
}

这里是把我们自定义的参数解析器,放到了customArgumentResolvers这个集合中

RequestMappingHandlerAdapter初始化

在这个类初始化的时候,会调用自己方法中的afterPropertiesSet()方法,为什么会调用这个方法就不说了,懂得都懂

/*** 获取所有的参数转换器** resolver结尾的,一般是参数转换器*/
if (this.argumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}

这里可以看到,也是有两个操作

getDefaultArgumentResolvers()

/*** 这是默认的参数处理器* @return*/
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();// Annotation-based argument resolutionresolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));resolvers.add(new RequestParamMapMethodArgumentResolver());resolvers.add(new PathVariableMethodArgumentResolver());resolvers.add(new PathVariableMapMethodArgumentResolver());resolvers.add(new MatrixVariableMethodArgumentResolver());resolvers.add(new MatrixVariableMapMethodArgumentResolver());resolvers.add(new ServletModelAttributeMethodProcessor(false));resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));resolvers.add(new RequestHeaderMapMethodArgumentResolver());resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new SessionAttributeMethodArgumentResolver());resolvers.add(new RequestAttributeMethodArgumentResolver());// Type-based argument resolutionresolvers.add(new ServletRequestMethodArgumentResolver());resolvers.add(new ServletResponseMethodArgumentResolver());resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RedirectAttributesMethodArgumentResolver());resolvers.add(new ModelMethodProcessor());resolvers.add(new MapMethodProcessor());resolvers.add(new ErrorsMethodArgumentResolver());resolvers.add(new SessionStatusMethodArgumentResolver());resolvers.add(new UriComponentsBuilderMethodArgumentResolver());// Custom arguments/*** 我们可以认为上面的都是spring默认的、自带的参数解析器,下面的getCustomArgumentResolvers()* 是获取我们自定义的* 所以:所有的参数解析器 = spring自带的 + 我们自定义的,现在全部放到了resolvers中*/if (getCustomArgumentResolvers() != null) {resolvers.addAll(getCustomArgumentResolvers());}// Catch-allresolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));resolvers.add(new ServletModelAttributeMethodProcessor(true));return resolvers;
}

这个方法很简单,就是返回spring自带的参数解析器 + 我们程序员自定义的参数解析器

new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);

这个代码中,接着也用到了组合设计模式

org.springframework.web.method.support.HandlerMethodArgumentResolverCompositeprivate final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<>();public HandlerMethodArgumentResolverComposite addResolvers(@Nullable List<? extends HandlerMethodArgumentResolver> resolvers) {if (resolvers != null) {for (HandlerMethodArgumentResolver resolver : resolvers) {this.argumentResolvers.add(resolver);}}return this;
}

上面的代码,我们来看下,代码中先 new HandlerMethodArgumentResolverComposite()创建了一个对象,然后将所有的resolvers放到了新new的对象中的argumentResolvers 中

组合设计模式在源码中的应用说完了,我们接着说是为什么这里要把所有的参数解析器,放到一个HandlerMethodArgumentResolverComposite对象中?

argumentResolvers在什么时候被使用?

为什么放到argumentResolvers这个对象中,其实就是为了使用
截止到目前,我们可以知道,在RequestMappingHandlerAdapter这个类初始化回调方法执行完之后,所有的参数解析器,都放在了argumentResolvers这个对象中,那在什么时候使用了呢?

在方法被调用的时候,我们知道,在方法被调用的时候,通过dispatchServlet会获取到一个参数解析器,然后去解析入参
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#invokeHandlerMethod
在前面博客中,介绍解析参数的链路中,有说到这个方法,

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ServletWebRequest webRequest = new ServletWebRequest(request, response);try {WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);/*** 下面这两个add操作是添加参数处理器或者返回值解析器的*/if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}if (this.returnValueHandlers != null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}return getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();}
}

这里可以看到,直接把argumentResolvers添加到了invocableMethod对象中
为什么会放到这个对象中?

org.springframework.web.method.support.InvocableHandlerMethod#getMethodArgumentValues
/*** Get the method argument values for the current request.* 从request中获取到当前方法的入参,也就是args*/
private Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {/*** 获取到所有的参数名*/MethodParameter[] parameters = getMethodParameters();Object[] args = new Object[parameters.length];for (int i = 0; i < parameters.length; i++) {MethodParameter parameter = parameters[i];parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);/*** 下面这行代码,看注释的意思是:从提供的参数列表中获取对应的值,但是从前面的调用链中会发现,入参的providedArgs是null*/args[i] = resolveProvidedArgument(parameter, providedArgs);if (args[i] != null) {continue;}/*** 遍历所有的参数解析器,看哪个解析器可以解析,如果参数解析器返回true,就表示可以解析* 如果我们要扩展参数解析器,就需要看下这里的逻辑* 需要学习下这里的argumentResolvers是在哪里赋值的*/if (this.argumentResolvers.supportsParameter(parameter)) {try {/*** 这里就是用parameter对应的解析器去解析该参数*/args[i] = this.argumentResolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);continue;}catch (Exception ex) {if (logger.isDebugEnabled()) {logger.debug(getArgumentResolutionErrorMessage("Failed to resolve", i), ex);}throw ex;}}if (args[i] == null) {throw new IllegalStateException("Could not resolve method parameter at index " +parameter.getParameterIndex() + " in " + parameter.getExecutable().toGenericString() +": " + getArgumentResolutionErrorMessage("No suitable resolver for", i));}}return args;
}

因为最底层对参数进行解析的时候,调用的就是InvocableHandlerMethod的方法,而ServletInvocableHandlerMethod是InvocableHandlerMethod的扩展类

以上就是springmvc源码中对组合设计模式的使用,以及自定义的参数解析器是如何被放入到spring容器中的

springmvc源码-我们自定义的参数解析器是如何放入到spring容器中的相关推荐

  1. spring MVC使用自定义的参数解析器解析参数

    目录 写在前面 编写自定义的参数解析器解析请求参数 项目结构 定义注解 实体类 controller 定义参数解析器 注册参数解析器 启动项目 发起请求查看结果 写在前面 如果还有小伙伴不知道spri ...

  2. SpringBoot--网上商城项目(自定义的参数解析器、购物车后台前台功能、商品详情页)

    目录 一.自定义的参数解析器 关于Mybatis-plus时间字段代码生成问题 报错信息:Caused by: java.lang.IllegalStateException: No typehand ...

  3. spring beans源码解读之--bean definiton解析器

    spring提供了有两种方式的bean definition解析器:PropertiesBeanDefinitionReader和XmLBeanDefinitionReader即属性文件格式的bean ...

  4. springmvc自定义参数解析器

    由于开发中一般使用参数提交方式是json格式,对于单个参数的传递使用无法接收只能自定义参数解析器处理 springmvc的自定义参数解析器实现HandlerMethodArgumentResolver ...

  5. springmvc自定义参数解析器/类型转换器

    概述 有些时候我们需要对GET请求的入参做自定义的处理,比较常见的就是字符串反序列化时间类型了,常用的像@DateTimeFormat注解,但是这需要在每个入参的属性上都加上这个注解,比较费手,那么我 ...

  6. spring 源码分析(1)-xml文件解析

    我们在最开始接触spring的时候,看到不少书spring入门的例子如下 ApplicationContext atx = new ClassPathXmlApplicationContext(&qu ...

  7. Spring MVC自定义类型转换器Converter、参数解析器HandlerMethodArgumentResolver

    文章目录 一.前言 二.类型转换器Converter 1.自定义类型转换器 三.参数解析器 1.自定义分页参数解析器 2.自定义注解参数解析器 一.前言 Spring MVC源码分析相关文章已出: S ...

  8. Spring自定义参数解析器

      虽然Spring提供了比较完善的参数解析器,但是对于一些特殊的数据类型我们还是需要进行特殊处理,这样会提高代码的复杂度,增加冗余的代码,降低代码可读性和可维护性.所以自定义参数解析器是一个很好的解 ...

  9. SpringMVC源码分析之AbstractHandlerMethodMapping体系扩展

    目录 前言: 一.AbstractHandlerMethodMapping体系概览 二.RequestMappingInfo 各种映射条件的初始化 RequestMappingInfo 三.Reque ...

  10. SpringMVC源码之参数解析绑定原理

    摘要 本文从源码层面简单讲解SpringMVC的参数绑定原理 SpringMVC参数绑定相关组件的初始化过程 在理解初始化之前,先来认识一个接口 HandlerMethodArgumentResolv ...

最新文章

  1. python __call__的作用,是可以将对象作为方法使用的关键 分析nn.Module源码
  2. AD-NeRF:用于说话人头部合成的音频驱动神经辐射场
  3. Python的库和资源
  4. GRE OVER IPSEC
  5. Go语言defer详解
  6. [译]星际争霸人工智能比赛——规则
  7. Apache VFS:基本介绍
  8. 今晚直播丨Oracle数据库SQL执行计划的取得和解析
  9. 第三:GitHub的使用(超详细)
  10. 17.Mongodb预分片(pre-split)/autosplit(chunk/jumbochunk相关)
  11. h2事务与mysql_H2数据库事务提交失败
  12. 【GitHub通过ssh方法下载详细配置过程】
  13. 数据字典的一个简单案例
  14. 单片机定时器TMOD与TCON详解!
  15. opencv学习(0)小知识点汇总
  16. 做为中层管理者的你,应该扮演什么角色
  17. ROG魔霸7Plus的CPU温度与 Armoury Crate 设置问题
  18. java 413错误,413错误——线上bug历险记
  19. 热电阻和热电偶的区别
  20. SQLServer日期函数的使用

热门文章

  1. 算法:Regular Expression Matching(正则表达式匹配)
  2. android 代码操作.db demo,Android实现商品展示效果
  3. 30. 与所有单词相关联的字串
  4. matlab仿真之大尺度衰落因子2--小区间
  5. 回归分析常数项t值没有显著异于零怎么办_洋蜜蜂统计辅导专题:回归分析关键词统计量须知...
  6. 【机器学习系列】EM算法第三讲:由Jensen Inequality推导EM算法
  7. 【从线性回归到BP神经网络】第四部分:BP神经网络
  8. 循环神经网络-Recurrent Neural Networks
  9. GBDT(MART) 迭代决策树入门教程 | 简介 写的非常好!!
  10. Reason of Random Initialization - Neural Networks