0%

##缘起
项目中需要用到kafka,公司的message queue sdk中已经封装了kafka的使用,在xml文件中进行配置就可以方便使用。但由于sdk的强依赖的问题,假如kafka链接失败会导致应用无法启动。所以就只能放弃sdk转为操作底层api操作kafka的启动监听以及关闭。

在使用的过程遇到了启动空指针以及关闭时TransactionManager已经被关闭的问题,同时Spring的初始化启动以及关闭过程是日常spring使用中最为关心的阶段,在前几篇文章介绍了Spring的初始化实现,那么结合前几篇文章以及对Spring关闭过程的解读来分析一下对于上述的场景遇到的问题可以怎么解决。

首先对Spring的Bean销毁过程的分析。

Spring的关闭过程

spring通过AbstractApplicateContext的close方法执行容器的关闭处理,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

public void close() {
synchronized (this.startupShutdownMonitor) {
doClose();
// If we registered a JVM shutdown hook, we don't need it anymore now:
// We've already explicitly closed the context.
if (this.shutdownHook != null) {
try {
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
}catch (IllegalStateException ex) {
// ignore - VM is already shutting down
}
}
}
}

protected void doClose() {
if (this.active.get() && this.closed.compareAndSet(false, true)) {
if (logger.isInfoEnabled()) {
logger.info("Closing " + this);
}

LiveBeansView.unregisterApplicationContext(this);

try {
// Publish shutdown event.
publishEvent(new ContextClosedEvent(this));
}
catch (Throwable ex) {
logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
}

// Stop all Lifecycle beans, to avoid delays during individual destruction.
try {
getLifecycleProcessor().onClose();
}
catch (Throwable ex) {
logger.warn("Exception thrown from LifecycleProcessor on context close", ex);
}

// Destroy all cached singletons in the context's BeanFactory.
destroyBeans();

// Close the state of this context itself.
closeBeanFactory();

// Let subclasses do some final clean-up if they wish...
onClose();

this.active.set(false);
}
}

可以看到最终的关闭处理是通过调用doClose方法执行,在onClose方法中处理了以下事情:

  1. 发布容器关闭事件ContextCloseEvent
  2. 调用lifeCycleProcessor的onClose方法(在AbstractBeanFactory的refresh的最后一步,调用的是LifeCycleProcessor接口的onRefresh方法,两者互相对应)
  3. 调用destroyBean方法
  4. 调用closeBeanFactory方法。是一个抽象方法,用于让子类关闭factory,比如将BeanFactory设置为null
  5. 调用onClose方法。这是一个空方法,目的是为了让子类在最后能够对关闭事件进行扩展

上述方法中destroyBean方法最为关键,bean的释放就是在这个方法中处理。

destoryBean

destoryBean最终是委托DefaultListableBeanFactory的destroySingletons方法进行bean的关闭处理,我们来看一下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

public void destroySingletons() {
if (logger.isDebugEnabled()) {
logger.debug("Destroying singletons in " + this);
}
synchronized (this.singletonObjects) {
this.singletonsCurrentlyInDestruction = true;
}

String[] disposableBeanNames;
synchronized (this.disposableBeans) {
// 获取到所有需要进行关闭释放的bean。
disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
}
for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
destroySingleton(disposableBeanNames[i]);
}

this.containedBeanMap.clear();
this.dependentBeanMap.clear();
this.dependenciesForBeanMap.clear();

synchronized (this.singletonObjects) {
this.singletonObjects.clear();
this.singletonFactories.clear();
this.earlySingletonObjects.clear();
this.registeredSingletons.clear();
this.singletonsCurrentlyInDestruction = false;
}
}

在Spring装载上下文,解析bean配置的时候,就会生成对应的disposableBeans数据。一个具体的bean在创建完成后,会在AbstractAutowireCapableBeanFactory的registerDisposableBeanIfNecessary中判断当前bean是否需要放入到disposableBeans中。

目前以下三种情况的bean会被注册到disposableBeans中:

  1. 实现了DisposableBean接口
  2. 在xml配置文件中自定义了destroy方法
  3. 实现了AutoCloseable接口。Mybatis的SqlSessionTemplate类就是实现了该接口,导致在spring容器关闭的时候,被反射调用了这个close方法。而SqlSessionTemplate类的生命周期是受Mybatis
    管理的,不能手动执行close。这也就是很经典的一个异常: Invocation of destroy method ‘close’ failed on bean with name ‘sqlSessionTemplate’
    第二和第三种方式,Spring都通过一个DisposableBeanAdapter将具体的bean包装成为一个实现了DisposableBean接口的实现类,使得在最终销毁Bean释放资源时,统一对DisposableBean操作。

Bean销毁的顺序

上面的代码逻辑中,会对取到的disposableBeans循环一个个调用destroyBean方法进行销毁与进行资源释放。那么是不是bean的销毁是有序的呢?能不能执行bean的销毁顺序呢?比如B要比A先释放?

可能这是一个很多人都会遇到的一个问题。Spring是不支持执行bean销毁的顺序,这个可以从Spring的一些官方讨论中得到:

假如你所销毁的资源有顺序关系的话,可以使用bean之间的依赖关系来实现这个目的,Spring在bean的销毁顺序上做了很多努力,但目前并不支持destroy的顺序配置。默认是先加载的bean后销毁,是一个反向操作。

所以你可以在destroyBean中看到一个关键逻辑,会先执行当前bean所依赖的bean的销毁操作。代码如下:

1
2
3
4
5
6
7
8
9
10
11
   //...
Set<String> dependencies = this.dependentBeanMap.remove(beanName);
if (dependencies != null) {
if (logger.isDebugEnabled()) {
logger.debug("Retrieved dependent beans for bean '" + beanName + "': " + dependencies);
}
for (String dependentBeanName : dependencies) {
destroySingleton(dependentBeanName);
}
}
//...

对于bean之间的依赖配置,可以使用下面方式:

  1. 在xml配置文件中通过视同depensOn的方式指定一个bean对另外一个bean的依赖
  2. 一个bean中的属性也是当前bean所依赖的实例

上文就是Spring 销毁bean的一个大概的逻辑,后续会编写一个实践案例来贯穿Spring的初始化与关闭

来源

Spring中Bean的关闭与资源释放

HandlerMethodArgumentResolver参数解析器

前文《spring-6-controller参数解析原理》介绍了 Controller 的整体参数解析原理。

本文章主要介绍集中介绍 HandlerMethodArgumentResolver在SpringMVC中的使用,介绍几个HandlerMethodArgumentResolver具体的使用情况,然后说明HandlerMethodArgumentResolver的注册来源以及如何自定义注册。

一、类图

springmvc_handlerMethod

二、HandlerMethodArgumentResolver 及其子类

2.1 HandlerMethodArgumentResolver

HandlerMethodArgumentResolver接口只有两个方法:

1
2
3
4
5
public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter var1);

Object resolveArgument(MethodParameter var1, ModelAndViewContainer var2, NativeWebRequest var3, WebDataBinderFactory var4) throws Exception;
}

2.2 AbstractMessageConverterMethodArgumentResolver

HandlerMethodArgumentResolver接口的抽象类: 仅仅引入了HttpMessageConverter,即具体的转换工作由这些HttpMessageConverter来完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {

private static final Set<HttpMethod> SUPPORTED_METHODS =
EnumSet.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.PATCH);

private static final Object NO_VALUE = new Object();

protected final Log logger = LogFactory.getLog(getClass());

protected final List<HttpMessageConverter<?>> messageConverters;

protected final List<MediaType> allSupportedMediaTypes;

private final RequestResponseBodyAdviceChain advice;
//略
}

2.3 AbstractMessageConverterMethodProcessor

AbstractMessageConverterMethodArgumentResolver 的抽象子类,加入了对响应数据进行转换的支持。
使其不仅可以用来转换请求数据,也可以用来转换响应数据。

下面简单介绍AbstractMessageConverterMethodProcessor 的子类:HttpEntityMethodProcessor和RequestResponseBodyMethodProcessor

2.3.1 HttpEntityMethodProcessor

AbstractMessageConverterMethodProcessor的子类,支持请求和响应的转换

1
2
3
4
5
6
7
8
9
10
11
12
13
public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodProcessor {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return (HttpEntity.class == parameter.getParameterType() ||
RequestEntity.class == parameter.getParameterType());
}

@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (HttpEntity.class.isAssignableFrom(returnType.getParameterType()) &&
!RequestEntity.class.isAssignableFrom(returnType.getParameterType()));
}
}

使用场景:

1
2
3
4
5
6
7
8
9
10
11
public class Test{
@RequestMapping(value="/test/http",method=RequestMethod.POST)
@ResponseBody
public Map<String,Object> testHttp(HttpEntity<String> httpEntity){
//略
}
@RequestMapping(value="/test/httpEntity",method=RequestMethod.GET)
public HttpEntity<String> testHttpEntity(){
//略
}
}

2.3.2 RequestResponseBodyMethodProcessor

AbstractMessageConverterMethodProcessor 的子类:支持@RequestBody@ResponseBody

1
2
3
4
5
6
7
8
9
10
11
12
13
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
@Override
public boolean supportsParameter(MethodParameter parameter) {
//查找参数中是否含有@RequestBody注解
return parameter.hasParameterAnnotation(RequestBody.class);
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
//查找参数中是否含有@RequestBody注解或者controller类上是否含有@RequestBody
return ((AnnotationUtils.findAnnotation(returnType.getContainingClass(), ResponseBody.class) != null) ||
(returnType.getMethodAnnotation(ResponseBody.class) != null));
}
}

使用场景如下:

1
2
3
4
5
6
7
8
9
10
11
public class Test{
@RequestMapping(value="/test/requestBody",method=RequestMethod.POST)
@ResponseBody
public Map<String,Object> testrequestBody(@RequestBody Map<String,Object> map1){
Map<String,Object> map=new HashMap<String,Object>();
map.put("name","lg");
map.put("age",23);
map.put("date",new Date());
return map;
}
}

2.3.3 HttpEntityMethodProcessor 解析过程:

通过 HttpMessageConverter 来进一步的判断是否支持HttpEntity<T>中我们想要的T类型以及是否支持相应的content-type,如public Map<String,Object> testHttp(HttpEntity<String> httpEntity) ,则会选择StringHttpMessageConverter来进行转换。具体的选择过程如下:

1
2
3
4
5
6
7
8
9
10

@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws IOException, HttpMediaTypeNotSupportedException {
HttpInputMessage inputMessage = createInputMessage(webRequest);
Type paramType = getHttpEntityType(parameter);

Object body = readWithMessageConverters(webRequest, parameter, paramType);
return new HttpEntity<Object>(body, inputMessage.getHeaders());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage,  
MethodParameter methodParam, Type targetType) throws IOException, HttpMediaTypeNotSupportedException {
MediaType contentType;
try {
contentType = inputMessage.getHeaders().getContentType();
}catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
if (contentType == null) {
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
Class<?> contextClass = methodParam.getContainingClass();
for (HttpMessageConverter<?> converter : this.messageConverters) {
if (converter instanceof GenericHttpMessageConverter) {
GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter;
if (genericConverter.canRead(targetType, contextClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + targetType + "] as \"" +
contentType + "\" using [" + converter + "]");
}
return genericConverter.read(targetType, contextClass, inputMessage);
}
}
Class<T> targetClass = (Class<T>) ResolvableType.forMethodParameter(methodParam, targetType).resolve(Object.class);

if (converter.canRead(targetClass, contentType)) {
if (logger.isDebugEnabled()) {
logger.debug("Reading [" + targetClass.getName() + "] as \"" +
contentType + "\" using [" + converter + "]");
return ((HttpMessageConverter<T>) converter).read(targetClass, inputMessage);
}
}

throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
}

2.3.4RequestResponseBodyMethodProcessor解析过程

RequestResponseBodyMethodProcessor也会使用相应的HttpMessageConverter来进行转换。如public Map<String,Object> testrequestBody(@RequestBody Map<String,Object> map1)则会选择MappingJackson2HttpMessageConverter或者MappingJacksonHttpMessageConverter来完成转换。

2.4 AbstractNamedValueMethodArgumentResolver

该类主要用于解析方法入参,他有多个子类,这里简单介绍其中四种类型

2.4.1 RequestParamMethodArgumentResolver

RequestParamMethodArgumentResolver支持的类型有,一种是含@RequestParam注解的参数,另一种就是简单类型,如Integer、String、Date、URI、URL、Locale等:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public boolean supportsParameter(MethodParameter parameter) {  
Class<?> paramType = parameter.getParameterType();
if (parameter.hasParameterAnnotation(RequestParam.class)) {
if (Map.class.isAssignableFrom(paramType)) {
String paramName = parameter.getParameterAnnotation(RequestParam.class).value();
return StringUtils.hasText(paramName);
}
else {
return true;
}
}
else {
if (parameter.hasParameterAnnotation(RequestPart.class)) {
return false;
}
else if (MultipartFile.class.equals(paramType) || "javax.servlet.http.Part".equals(paramType.getName())) {
return true;
}
else if (this.useDefaultResolution) {
return BeanUtils.isSimpleProperty(paramType);
}
else {
return false;
}
}
}

BeanUtils.isSimpleProperty(paramType)判断是否是简单类型的具体内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
public static boolean isSimpleProperty(Class<?> clazz) {
Assert.notNull(clazz, "Class must not be null");
return isSimpleValueType(clazz) || (clazz.isArray() && isSimpleValueType(clazz.getComponentType()));
}

public static boolean isSimpleValueType(Class<?> clazz) {
return (ClassUtils.isPrimitiveOrWrapper(clazz) || clazz.isEnum() ||
CharSequence.class.isAssignableFrom(clazz) ||
Number.class.isAssignableFrom(clazz) ||
Date.class.isAssignableFrom(clazz) ||
URI.class == clazz || URL.class == clazz ||
Locale.class == clazz || Class.class == clazz);
}

即当请求为 http://localhost:8080/test?name=abc时,处理函数若为test(String name),则对name的解析就是采用RequestParamMethodArgumentResolver来解析的。

2.4.2 RequestHeaderMethodArgumentResolver

主要用来处理含有@RequestHeader注解的参数,但同时该参数又不是Map类型。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override  
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestHeader.class)
&& !Map.class.isAssignableFrom(parameter.getParameterType());
}

@Override
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
String[] headerValues = request.getHeaderValues(name);
if (headerValues != null) {
return (headerValues.length == 1 ? headerValues[0] : headerValues);
}
else {
return null;
}
}

使用场景:

1
2
3
4
5
@RequestMapping(value="/test/requestHeader",method=RequestMethod.GET)  
@ResponseBody
public Map<String,Object> testrequestHeader(@RequestHeader String Accept){
...
}

2.4.3RequestHeaderMapMethodArgumentResolver

用来获取所有的header信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgumentResolver {

//这里已经写明白了,要求参数必须含有@RequestHeader注解,并且是Map类型
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestHeader.class)
&& Map.class.isAssignableFrom(parameter.getParameterType());
}

@Override
public Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws Exception {

Class<?> paramType = parameter.getParameterType();

if (MultiValueMap.class.isAssignableFrom(paramType)) {
MultiValueMap<String, String> result;
if (HttpHeaders.class.isAssignableFrom(paramType)) {
result = new HttpHeaders();
}
else {
result = new LinkedMultiValueMap<String, String>();
}
for (Iterator<String> iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
String headerName = iterator.next();
for (String headerValue : webRequest.getHeaderValues(headerName)) {
result.add(headerName, headerValue);
}
}
return result;
}
else {
Map<String, String> result = new LinkedHashMap<String, String>();
for (Iterator<String> iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
String headerName = iterator.next();
String headerValue = webRequest.getHeader(headerName);
result.put(headerName, headerValue);
}
return result;
}
}
}

从上面的解析过程可以看出,参数类型可以是普通的Map类型,也可以是MultiValueMap或者进一步的HttpHeaders,他们与普通Map类型的区别是他们对value值后者们是以List形式存放,前者是以String形式存放。

使用场景:

1
2
3
4
5
6
7
@RequestMapping(value="/test/requestHeader",method=RequestMethod.GET)  
@ResponseBody
public Map<String,Object> testrequestHeader(@RequestHeader Map<String,Object> map1){
}

public Map<String,Object> testrequestHeader(@RequestHeader MultiValueMap<String,Object> map1){
}

2.4.4 PathVariableMethodArgumentResolver

主要针对含有@PathVariable的参数,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
public boolean supportsParameter(MethodParameter parameter) {
if (!parameter.hasParameterAnnotation(PathVariable.class)) {
return false;
}
if (Map.class.isAssignableFrom(parameter.getParameterType())) {
String paramName = parameter.getParameterAnnotation(PathVariable.class).value();
return StringUtils.hasText(paramName);
}
return true;
}

@Override
@SuppressWarnings("unchecked")
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
Map<String, String> uriTemplateVars =
(Map<String, String>) request.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);
return (uriTemplateVars != null) ? uriTemplateVars.get(name) : null;
}

对于支持的类型也说明的很详细。首先必须含有@PathVariable注解,其次如果是Map类型,必须要指定@PathVariable的值,即这个 ArgumentResolver只能获取一个uri变量。

2.4.5 PathVariableMapMethodArgumentResolver

获取多个uri变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override
public boolean supportsParameter(MethodParameter parameter) {
PathVariable annot = parameter.getParameterAnnotation(PathVariable.class);
return ((annot != null) && (Map.class.isAssignableFrom(parameter.getParameterType()))
&& (!StringUtils.hasText(annot.value())));
}

public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

@SuppressWarnings("unchecked")
Map<String, String> uriTemplateVars =
(Map<String, String>) webRequest.getAttribute(
HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);

if (!CollectionUtils.isEmpty(uriTemplateVars)) {
return new LinkedHashMap<String, String>(uriTemplateVars);
}
else {
return Collections.emptyMap();
}
}

它要求必须含有@PathVariable注解,并且必须是Map类型,并且@PathVariable注解的value没有值。同时我们可以从PathVariableMapMethodArgumentResolverPathVariableMethodArgumentResolver上面看出,他们的取值都是从request的属性上进行获取的webRequest.getAttribute( HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST);也就是说,在解析完@RequestMapping匹配工作后,便将这些参数设置进request的属性上,属性名为HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE。

三、HandlerMethodArgumentResolver注册来源

至此,我们就要说明下HandlerMethodArgumentResolver的注册来源:
它的来源分为两部分,一部分spring默认的HandlerMethodArgumentResolver,另一部分就是我们自定义的HandlerMethodArgumentResolver。

先看mvc:annotation-driven中配置自定义的HandlerMethodArgumentResolver:

1
2
3
4
5
<mvc:annotation-driven >
<mvc:argument-resolvers>
<bean class="xxx"></bean>
</mvc:argument-resolvers>
</mvc:annotation-driven>

在mvc:argument-resolvers标签下配置相应的自定义的HandlerMethodArgumentResolver。
然后在mvc:annotation-driven的注解驱动类AnnotationDrivenBeanDefinitionParser中会有这样的代码:

1
2
3
4
5
6
ManagedList<?> argumentResolvers = getArgumentResolvers(element, parserContext);

//略
if (argumentResolvers != null) {
handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
}

其中getArgumentResolvers就是获取我们自定义的HandlerMethodArgumentResolver

1
2
3
4
5
6
7
8
private ManagedList<?> getArgumentResolvers(Element element, ParserContext parserContext) {
Element resolversElement = DomUtils.getChildElementByTagName(element, "argument-resolvers");
if (resolversElement != null) {
ManagedList<BeanDefinitionHolder> argumentResolvers = extractBeanSubElements(resolversElement, parserContext);
return wrapWebArgumentResolverBeanDefs(argumentResolvers, parserContext);
}
return null;
}

从上面的代码可以看出,获取我们自定义的HandlerMethodArgumentResolver然后把它设置进RequestMappingHandlerAdapter的customArgumentResolvers参数中,RequestMappingHandlerAdapter有两个与HandlerMethodArgumentResolver有关的参数:

1
2
private List<HandlerMethodArgumentResolver> customArgumentResolvers;  
private HandlerMethodArgumentResolverComposite argumentResolvers;

HandlerMethodArgumentResolverComposite 也仅仅是内部存放一个List<HandlerMethodArgumentResolver>集合,同时本身又继承HandlerMethodArgumentResolver,所以它的实现都是靠内部的List<HandlerMethodArgumentResolver>集合来实现的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
private final List<HandlerMethodArgumentResolver> argumentResolvers =
new LinkedList<HandlerMethodArgumentResolver>();

//使用了适合高并发的ConcurrentHashMap来进行缓存
private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
new ConcurrentHashMap<MethodParameter, HandlerMethodArgumentResolver>(256);


/**
* Return a read-only list with the contained resolvers, or an empty list.
*/
public List<HandlerMethodArgumentResolver> getResolvers() {
return Collections.unmodifiableList(this.argumentResolvers);
}

/**
* Whether the given {@linkplain MethodParameter method parameter} is supported by any registered
* {@link HandlerMethodArgumentResolver}.
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
return getArgumentResolver(parameter) != null;
}

/**
* Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it.
* @exception IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found.
*/
@Override
public Object resolveArgument(
MethodParameter parameter, ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
throws Exception {

HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]");
return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
}

/**
* Find a registered {@link HandlerMethodArgumentResolver} that supports the given method parameter.
*/
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
if (logger.isTraceEnabled()) {
logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" +
parameter.getGenericParameterType() + "]");
}
if (methodArgumentResolver.supportsParameter(parameter)) {
result = methodArgumentResolver;
this.argumentResolverCache.put(parameter, result);
break;
}
}
}
return result;
}

在RequestMappingHandlerAdapter完成参数设置后,会调用afterPropertiesSet方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public void afterPropertiesSet() {
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
initControllerAdviceCache();
}

getDefaultArgumentResolvers方法完成了所有的HandlerMethodArgumentResolver的汇总,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();

// 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()));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters()));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));

// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters()));
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
//获取我们自定义的HandlerMethodArgumentResolver
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}

// Catch-all
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));

return resolvers;
}

不仅汇总了spring默认的,同时加进来我们自定义的HandlerMethodArgumentResolver。

参考

SpringMVC源码总结(九)HandlerMethodArgumentResolver介绍

综述

Controller方法的参数类型可以是基本类型,也可以是封装后的普通Java类型。
若这个普通Java类型没有声明任何注解,则意味着它的每一个属性都需要到Request中去查找对应的请求参数。
众所周知,无论客户端传入的是什么类型的请求参数,最终都要以字节的形式传给服务端。
而服务端通过Request的getParameter方法取到的参数也都是字符串形式的结果。
所以,需要有一个把字符串形式的参数转换成服务端真正需要的类型的转换工具,在spring中这个转换工具为WebDataBinder。

WebDataBinder 类图

参考

SpringMVC中WebDataBinder的应用及原理