100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > springmvc源码-我们自定义的参数解析器是如何放入到spring容器中的

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

时间:2018-12-27 06:09:16

相关推荐

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

之前的博客中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;}

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

@Overrideprotected 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.WebMvcConfigurerCompositeprivate 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的实现类

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

@Overridepublic 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

在前面博客中,介绍解析参数的链路中,有说到这个方法,

@Nullableprotected 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容器中的

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。