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

本文详细探讨了Spring MVC中自定义参数解析器如何被放入Spring容器的过程。从`WebMvcConfigurationSupport#requestMappingHandlerAdapter`开始,通过`getArgumentResolvers()`和`adapter.setCustomArgumentResolvers()`方法,解析器被添加到容器中。在`RequestMappingHandlerAdapter`初始化时,`getDefaultArgumentResolvers()`与`new HandlerMethodArgumentResolverComposite().addResolvers(resolvers)`结合使用,利用组合设计模式整合了所有解析器。最后,这些解析器在方法调用时通过` InvocableHandlerMethod`进行参数解析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前的博客中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 中的一个集合中,WebMvcConfigurerCompositeWebMvcConfigurer是什么关系?
前者是后者的实现类

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值