@RequestBody 和 @ResponseBody 是实际开发中很常用的两个注解,通常用来解析和响应JSON,用起来十分的方便,这两个注解的背后是如何实现的?
源码版本
SpringBoot 2.1.3.RELEASE
RequestResponseBodyMethodProcessor
Resolves method arguments annotated with @RequestBody and handles return values from methods annotated with @ResponseBody by reading and writing to the body of the request or response with an HttpMessageConverter.
An @RequestBody method argument is also validated if it is annotated with @javax.validation.Valid. In case of validation failure, MethodArgumentNotValidException is raised and results in an HTTP 400 response status code if DefaultHandlerExceptionResolver is configured.
简单来说,这个类用来解析@RequestBody
的参数和处理 @ResponseBody
返回值,通过 HttpMessageConverter 这个接口来实现。
如果@RequestBody
标记的参数包含@Valid
,还会对这个参数进行校验。
继承关系
HandlerMethodArgumentResolver 和 HandlerMethodReturnValueHandler 分别是Spring的参数处理器和返回值处理器
- HandlerMethodArgumentResolver
1
2
3
4
5
6
7
8public interface HandlerMethodArgumentResolver {
boolean supportsParameter(MethodParameter parameter);
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
Spring的参数解析器接口,supportsParameter() 方法用于判断解析器是否支持当前Controller方法的参数,resolveArgument() 则是将Request解析为Controller方法对应的参数Bean
- HandlerMethodReturnValueHandler
1
2
3
4
5
6
7
8public interface HandlerMethodReturnValueHandler {
boolean supportsReturnType(MethodParameter returnType);
void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
同理这个接口将Controller方法返回的对象,封装为Response
我们在实际开发时,也可以实现这两个接口自定义自己的参数解析和响应处理,RequestResponseBodyMethodProcessor 实现了这两个接口,既做了参数解析器也做了响应处理器。
RequestResponseBodyMethodProcessor 源码分析
我们来看一下 RequestResponseBodyMethodProcessor 是如何工作的,以解析参数为例
- resolveArgument
1 |
|
作为参数解析器,RequestResponseBodyMethodProcessor 支持所有标记@RequestBody的参数。在resolveArgument()方法中,通过调用readWithMessageConverters() 将 Request 转为对应 arg。我们来看一下 readWithMessageConverters() 到底做了什么
- readWithMessageConverters
1 | protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter, |
上述代码核心逻辑就是遍历当前解析中配置的所有 HttpMessageConverter,如果某个Converter可以解析当前的 contentType,就把转换工作交给他去进行。
之前做过将默认解析替换为fastjson,当时就是添加一个FastJson实现的HttpMessageConverter,但是那时候并不理解这么做是为了什么,现在才恍然大悟…
- handleReturnValue
RequestResponseBodyMethodProcessor 的Response处理逻辑和解析逻辑类似,找到一个支持的HttpMessageConverter,把响应工作交给他,感兴趣的童鞋可以自己找下源码。
RequestResponseBodyMethodProcessor 是怎么被调用的
上面讲了 RequestResponseBodyMethodProcessor 做了参数解析和响应处理的工作,那么他在Spring框架中是怎么被调用的,我们来看一下
如图,RequestMappingHandlerAdapter 的resolvers(Request解析器)、handlers(Response处理器)还有 ExceptionHandlerExceptionResolver 的handlers 调用了 RequestResponseBodyMethodProcessor
RequestMappingHandlerAdapter
我们只分析一下 RequestMappingHandlerAdapter ,该类对所有标记 @RequestMapping的注解进行解析和响应
在WebMvcConfigurationSupport中,配置了该Bean,将其加入到Spring容器中,我们自定义的参数解析、响应解析、和HttpMessageConvert 通过上图的方法set到 RequestMappingHandlerAdapter 中。
1 |
|
继续说 RequestMappingHandlerAdapter ,getDefaultArgumentResolvers() 封装了SpringBoot中的默认参数解析器,其中就有我们的本节所讲的 RequestResponseBodyMethodProcessor ,在afterPropertiesSet() 方法中调用了该方法
1 |
|
RequestResponseBodyMethodProcessor 何时被调用
上面铺垫了这么多,终于来了
RequestMappingHandlerAdapter 的 invokeHandlerMethod 中 构建了 invocableMethod 对象并将所有的解析器和处理器封装到该对象,通过invocableMethod.invokeAndHandle() 进行对请求的解析,对controller的调用,以及响应的处理
invocableMethod.invokeAndHandle() 中是怎么样实现的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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 参数解析,并反射调用controller方法,获取方法返回值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 下面就是对Response的处理
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 调用参数解析器获取调用controller 所需的参数
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
if (logger.isTraceEnabled()) {
logger.trace("Arguments: " + Arrays.toString(args));
}
// 反射调用 controller
return doInvoke(args);
}
protected Object[] getMethodArgumentValues(NativeWebRequest request, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
if (ObjectUtils.isEmpty(getMethodParameters())) {
return EMPTY_ARGS;
}
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);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
// 这里的 resolvers 是一个封装了所有参数解析器的包装类,遍历所有解析器,如果不能找到支持当前参数的,抛出异常
// 如果找到当前参数对应的解析器,则缓存起来,在下面的 resolvers.resolveArgument 时,直接使用
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
// 调用参数解析器
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled..
if (logger.isDebugEnabled()) {
String error = ex.getMessage();
if (error != null && !error.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, error));
}
}
throw ex;
}
}
return args;
}
invokeAndHandle() 里做了三件事
- 将请求中解析为Controller中指定的参数
- 用解析好的参数反射调用 Controller 方法
- 处理响应
一次Http请求经历了什么
回过头来再看,这时候我们发一个请求,在 RequestMappingHandlerAdapter 的 invokeHandlerMethod()中 debug一下,看一下线程栈是什么样的
简单画一张图来表示一下
END
欢迎各位给出意见和指正