之前的博客中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 resolution
resolvers.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 resolution
resolvers.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-all
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
这个方法很简单,就是返回spring自带的参数解析器 + 我们程序员自定义的参数解析器
new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
这个代码中,接着也用到了组合设计模式
org.springframework.web.method.support.HandlerMethodArgumentResolverComposite
private 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容器中的