介绍 SpringMvc 如何处理参数解析 和 如何处理返回值的。
文章最后列举了HandlerMethodArgumentResolver 接口和 HandlerMethodReturnValueHandler 接口的实现类列表
SpringMVC请求过程
- 前端请求 首先被 DispatcherServlet 截获,DispatcherServlet 通过 handlerMapping 获得HandlerExecutionChain,然后获得 HandlerAdapter。
- HandlerAdapter 在内部对于每个请求,都会实例化一个 ServletInvocableHandlerMethod 进行处理,ServletInvocableHandlerMethod 在进行处理的时候,会分两部分(参数值和返回值)分别对请求跟响应进行处理。
- 之后 HandlerAdapter 得到 ModelAndView ,然后做相应的处理。
ServletInvocableHandlerMethod 分析
ServletInvocableHandlerMethod 在处理时,将 方法参数的处理 交给 HandlerMethodArgumentResolver 处理,而方法的返回值会交给 HandlerMethodReturnValueHandler 处理。
- HandlerMethodArgumentResolver 请求方法参数的处理
- HandlerMethodReturnValueHandler 响应返回值的处理
| 12
 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 class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
 
 
 
 public Object invokeForRequest(
 NativeWebRequest request,
 @Nullable ModelAndViewContainer mavContainer,
 Object... providedArgs) throws Exception {
 
 Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
 if (logger.isTraceEnabled()) {
 logger.trace("Arguments: " + Arrays.toString(args));
 }
 return doInvoke(args);
 }
 
 
 public void invokeAndHandle(
 ServletWebRequest webRequest,
 ModelAndViewContainer mavContainer,
 Object... providedArgs) throws Exception {
 
 Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
 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;
 }
 }
 
 | 
RequestResponseBodyMethodProcessor详解
本文以 RequestResponseBodyMethodProcessor 为例讲解 。

HandlerMethodArgumentResolver接口
定义了两个方法:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | public interface HandlerMethodArgumentResolver {
 boolean supportsParameter(MethodParameter parameter);
 
 @Nullable
 Object resolveArgument(
 MethodParameter parameter,
 @Nullable ModelAndViewContainer mavContainer,
 NativeWebRequest webRequest,
 @Nullable WebDataBinderFactory binderFactory) throws Exception;
 
 }
 
 | 
HandlerMethodReturnValueHandler接口
定义了两个方法:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | public interface HandlerMethodReturnValueHandler {
 boolean supportsReturnType(MethodParameter returnType);
 
 void handleReturnValue(
 @Nullable Object returnValue,
 MethodParameter returnType,
 ModelAndViewContainer mavContainer,
 NativeWebRequest webRequest) throws Exception;
 
 }
 
 | 
AbstractMessageConverterMethodArgumentResolver
HandlerMethodArgumentResolver接口的抽象类: 仅仅引入了HttpMessageConverter,即具体的转换工作由这些HttpMessageConverter来完成。 
简单介绍AbstractMessageConverterMethodArgumentResolver中其他关联的类: 
- HttpMessageConverter : 消息转换器。将 Request的 body 转换为想要的结果。常接触到的例子。FastJsonHttpMessageConverter 将 json字符串转换为对应的类对象。
- RequestBodyAdvice: 在消息转换器的作用前后执行。常用于 请求的加解密,与ControllerAdvice注解同时使用。
AbstractMessageConverterMethodArgumentResolver源码:
| 12
 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
 
 | public abstract class AbstractMessageConverterMethodArgumentResolver implements HandlerMethodArgumentResolver {
 
 @Nullable
 protected <T> Object readWithMessageConverters(
 HttpInputMessage inputMessage,
 MethodParameter parameter,
 Type targetType)
 throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
 
 MediaType contentType;
 boolean noContentType = false;
 
 try {
 contentType = inputMessage.getHeaders().getContentType();
 }
 catch (InvalidMediaTypeException ex) {
 throw new HttpMediaTypeNotSupportedException(ex.getMessage());
 }
 if (contentType == null) {
 noContentType = true;
 contentType = MediaType.APPLICATION_OCTET_STREAM;
 }
 
 Class<?> contextClass = parameter.getContainingClass();
 Class<T> targetClass = (targetType instanceof Class ? (Class<T>) targetType : null);
 if (targetClass == null) {
 ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
 targetClass = (Class<T>) resolvableType.resolve();
 }
 
 HttpMethod httpMethod = (inputMessage instanceof HttpRequest ? ((HttpRequest) inputMessage).getMethod() : null);
 Object body = NO_VALUE;
 
 
 
 EmptyBodyCheckingHttpInputMessage message;
 try {
 message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
 
 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))) {
 if (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;
 }
 }
 }
 catch (IOException ex) {
 throw new HttpMessageNotReadableException("I/O error while reading input message", ex, inputMessage);
 }
 
 if (body == NO_VALUE) {
 if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) ||
 (noContentType && !message.hasBody())) {
 return null;
 }
 throw new HttpMediaTypeNotSupportedException(contentType, this.allSupportedMediaTypes);
 }
 
 MediaType selectedContentType = contentType;
 Object theBody = body;
 LogFormatUtils.traceDebug(logger, traceOn -> {
 String formatted = LogFormatUtils.formatValue(theBody, !traceOn);
 return "Read \"" + selectedContentType + "\" to [" + formatted + "]";
 });
 
 return body;
 }
 }
 
 | 
AbstractMessageConverterMethodProcessor抽象类
在 AbstractMessageConverterMethodArgumentResolver抽象类 的基础上,添加HandlerMethodReturnValueHandler接口。
因此 AbstractMessageConverterMethodProcessor类 不仅可以用来转换请求数据,也可以用来转换响应数据。 
RequestResponseBodyMethodProcessor类
继承自 AbstractMessageConverterMethodProcessor,专注解析 包含@RequestBody 和 @ResponseBody注解的方法。
| 12
 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
 
 | public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
 
 @Override
 public boolean supportsParameter(MethodParameter parameter) {
 return parameter.hasParameterAnnotation(RequestBody.class);
 }
 
 @Override
 public boolean supportsReturnType(MethodParameter returnType) {
 return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class)
 || returnType.hasMethodAnnotation(ResponseBody.class));
 }
 
 @Override
 public Object resolveArgument(
 MethodParameter parameter,
 @Nullable ModelAndViewContainer mavContainer,
 NativeWebRequest webRequest,
 @Nullable WebDataBinderFactory binderFactory) throws Exception {
 
 
 
 
 
 }
 
 @Override
 protected <T> Object readWithMessageConverters(
 NativeWebRequest webRequest,
 MethodParameter parameter,
 Type paramType)
 throws IOException, HttpMediaTypeNotSupportedException,HttpMessageNotReadableException
 {
 
 
 
 
 }
 
 @Override
 public void handleReturnValue(
 @Nullable Object returnValue,
 MethodParameter returnType,
 ModelAndViewContainer mavContainer,
 NativeWebRequest webRequest)
 throws IOException, HttpMediaTypeNotAcceptableException,HttpMessageNotWritableException {
 
 
 
 }
 
 | 
相关接口的实现类总览
HandlerMethodArgumentResolver实现类
| 实现类 | 说明 | 
| RequestParamMethodArgumentResolver | 支持带有@RequestParam注解的参数或带有MultipartFile类型的参数 | 
| RequestParamMapMethodArgumentResolver | 支持带有@RequestParam注解的参数 && @RequestParam注解的属性value存在 && 参数类型是实现Map接口的属性 | 
| PathVariableMethodArgumentResolver | 支持带有@PathVariable注解的参数 且如果参数实现了Map接口,@PathVariable注解需带有value属性 | 
| MatrixVariableMethodArgumentResolver | 支持带有@MatrixVariable注解的参数 且如果参数实现了Map接口,@MatrixVariable注解需带有value属性 | 
| RequestResponseBodyMethodProcessor | 本章已分析过 | 
| ServletRequestMethodArgumentResolver | 参数类型是实现或继承或是WebRequest、ServletRequest、MultipartRequest、HttpSession、Principal、Locale、TimeZone、InputStream、Reader、HttpMethod这些类。(这就是为何我们在Controller中的方法里添加一个HttpServletRequest参数,Spring会为我们自动获得HttpServletRequest对象的原因) | 
| ServletResponseMethodArgumentResolver | 参数类型是实现或继承或是ServletResponse、OutputStream、Writer这些类 | 
| RedirectAttributesMethodArgumentResolver | 参数是实现了RedirectAttributes接口的类 | 
| HttpEntityMethodProcessor | 参数类型是HttpEntity | 
HandlerMethodReturnValueHandler实现类
| 实现类 | 说明 | 
| ModelAndViewMethodReturnValueHandler | 返回值类型是ModelAndView或其子类 | 
| ModelMethodProcessor | 返回值类型是Model或其子类 | 
| ViewMethodReturnValueHandler | 返回值类型是View或其子类 | 
| HttpHeadersReturnValueHandler | 返回值类型是HttpHeaders或其子类 | 
| ModelAttributeMethodProcessor | 返回值有@ModelAttribute注解 | 
| ViewNameMethodReturnValueHandler | 返回值是void或String | 
接口总览小结
从名字我们也看的出来
- Resolver结尾的是实现了HandlerMethodArgumentResolver接口的类
- Handler结尾的是实现了HandlerMethodReturnValueHandler接口的类
- Processor结尾的是实现了HandlerMethodArgumentResolver和HandlerMethodReturnValueHandler的类
来源
https://www.cnblogs.com/fangjian0423/p/springMVC-request-param-analysis.html