Spring源码分析(10)——SpringMVC参数绑定和返回值解析

HTTP请求到达Controller层时,需要将请求中的参数转换成java可识别的参数,等Controller处理完成后,又需要将java的返回值转换为HTTP内容返回给用户。

先回顾SpringMVC调用Controller的入口位置,这里会调用HandlerAdapter,让HandlerAdapter调用实际的Controller方法。但这个方法可能包含不同的参数和返回值,就需要对参数和返回值进行绑定。

DispatcherServlet.java
1
2
3
4
5
6
7
8
9
10
11
12
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// 获取HandlerAdapter
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 调用拦截器的preHandle
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 实际调用Controller
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 调用拦截器的postHandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
}

一、初始化

常用的一个URL对应一个方法使用的是RequestMappingHandlerAdapter,这也是要研究的重点。这个类在afterPropertiesSet()方法会初始化参数绑定工具HandlerMethodArgumentResolver和返回值绑定工具HandlerMethodReturnValueHandler

RequestMappingHandlerAdapter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Override
public void afterPropertiesSet() {
initControllerAdviceCache();

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);
}
}

二、普通方法的参数绑定过程

一路跟着doDispatch方法的handle下去,在使用反射调用实际Handle方法之前,会对方法的每次参数进行绑定,待Handle处理完成之后,又会对返回值进行处理。

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
@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
@Override
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
......
mav = invokeHandlerMethod(request, response, handlerMethod);
......
return mav;
}
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
......
invocableMethod.invokeAndHandle(webRequest, mavContainer);
......
}
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,Object... providedArgs) throws Exception {
// 调用方法并获得返回值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
......
// 对返回值进行处理
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
......
}
protected 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++) {
// 找到能处理该参数的resolver
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
// 参数绑定
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
return args;
}

要使用哪个resolver来绑定参数呢?在RequestMappingHandlerAdapter初始化时已经默认定义了一系列resolver,如果任意一个能支持解析参数,就会使用它来解析参数。

常用的HandlerMethodArgumentResolver有这些:

  • RequestResponseBodyMethodProcessor:解析注释了@RequestBody类型的参数

  • PathVariableMethodArgumentResolver:解析注释了@PathVariable而且不是Map类型的参数

  • RequestHeaderMethodArgumentResolver:解析注释了@RequestHeader的Map类型的参数

  • RequestParamMethodArgumentResolver:解析注释了@RequestParam的参数

  • ModelMethodProcessor:解析Model类型的参数

RequestMappingHandlerAdapter
HandlerMethodArgumentResolver
Controller Method
根据不同的注解或参数类型来选择参数处理器
所有处理器都定义在RequestMappingHandlerAdapter中
HandlerMethodReturnValueHandler
根据不同的注解或返回值来选择返回值处理器
所有处理器都定义在RequestMappingHandlerAdapter
实际调用Controller方法
拦截器
preHandle
拦截器
postHandle

三、RESTful方法的参数绑定过程

如果一个方法使用了@ResponseBody注解,将会使用RequestResponseBodyMethodProcessor来对参数和返回值进行处理。

以RequestResponseBodyMethodProcessor的参数绑定举例:从messageConverters中取出一个合适的来进行参数绑定,这和选择ArgumentResolver的套路如出一辙。

RequestResponseBodyMethodProcessor.java
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
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
......
Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType());
......
return adaptArgumentIfNecessary(arg, parameter);
}
@Override
protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter,
Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
......
Object arg = readWithMessageConverters(inputMessage, parameter, paramType);
......
return arg;
}
@Nullable
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {

for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
GenericHttpMessageConverter<?> genericConverter =
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
(targetClass != null && converter.canRead(targetClass, contentType))) {
cif (message.hasBody()) {
HttpInputMessage msgToUse = getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterType);
}
else {
body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterType);
}
break;
}
}
return body;
}

哦对了,这个messageConverter一般需要自己进行配置,一般这样配置:

servlet.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="mappingJacksonHttpMessageConverter"/>
</list>
</property>
</bean>
<bean id="mappingJacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
RequestMappingHandlerAdapter
HandlerMethodArgumentResolver
HttpMessageConverter
Controller Method
根据不同的注解或参数来选择参数处理器
默认是RequestResponseBodyMethodProcessor
HandlerMethodReturnValueHandler
HttpMessageConverter
根据accept-type类型来选择Converter
一般是MappingJackson2HttpMessageConverter
根据accept-type类型来选择Converter
一般是MappingJackson2HttpMessageConverter
根据不同的注解或返回值来选择返回值处理器
默认是RequestResponseBodyMethodProcessor
实际调用Controller方法
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×