【Spring MVC】MVC请求的处理过程一览-HandlerMethodArgumentResolver解析参数过程【二】
1 前言
上节【Spring MVC】MVC请求的处理过程一览-HandlerMethodArgumentResolver解析参数过程【一】,我们看了六个解析器,其中 RequestParamMethodArgumentResolver、PathVariableMethodArgumentResolver、MatrixVariableMethodArgumentResolver 这三个解析器都是继承了 AbstractNamedValueMethodArgumentResolver 是带有参数值转换器的功能的,而另外三个相对应的Map 类型的 RequestParamMapMethodArgumentResolver、PathVariableMapMethodArgumentResolver、MatrixVariableMapMethodArgumentResolver 这三个解析器是没有转换功能的,本节我们继续看几个转换器。
2 解析器解析
2.1 ServletModelAttributeMethodProcessor
这个解析器也是平时常用的,他能将参数转换为我们的某个对象,比如:
// receiveByModelAttribute?orderNo=111&orderNoList=222&orderNoList=333 @GetMapping("/receiveByModelAttribute") public OrderDto receiveByModelAttribute(@ModelAttribute OrderDto orderDto) { log.info("orderDto={}", orderDto); return orderDto; }
解析的效果:

上述示例也可以不写 @ModelAttribute 注解,我们回顾下它的定义其实就可以知道:
// ServletModelAttributeMethodProcessor 继承了 ModelAttributeMethodProcessor public boolean supportsParameter(MethodParameter parameter) { // 含有 @ModelAttribute注解的参数 annotationNotRequired默认是 false 也就是没有 @ModelAttribute 注解的情况下不是基本类型的 不是字符串等类型的 return (parameter.hasParameterAnnotation(ModelAttribute.class) || (this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType()))); }
我们先看下 ServletModelAttributeMethodProcessor 的类结构,他是继续了 ModelAttributeMethodProcessor:

那我们看下解析参数的过程 resolveArgument :
// ModelAttributeMethodProcessor#resolveArgument // parameter 方法参数对象 // mavContainer 传递上下文的容器对象 // webRequest 请求对象 // binderFactory 绑定工厂对象 public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer"); Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory"); // 获取参数的名称 @ModelAttribute 注解里的 name值 没有的话就取你方法定义的参数名字 比如我们上边例子里的 orderDto String name = ModelFactory.getNameForParameter(parameter); // 是否绑定的判断 默认都会进行绑定的 // 这个什么意思呢?比如我们上边的例子 @ModelAttribute(binding = false) OrderDto orderDto // 增加一个 binding = false 他就只会创建一个空的 OrderDto 对象,不进行参数值的绑定了 // 其实就是做个标记,下边会根据这个标记判断要不要进行参数绑定 ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class); if (ann != null) { mavContainer.setBinding(name, ann.binding()); } // attribute 就是你的参数对象 比如 OrderDto 对象 Object attribute = null; // 绑定结果 BindingResult bindingResult = null; // 如果已经有了这个参数对象 attribute 那就直接取出来 if (mavContainer.containsAttribute(name)) { attribute = mavContainer.getModel().get(name); } else { // 创建参数对象 创建一个空对象出来 反射获取你的参数构造器 然后进行创建 // Create attribute instance try { attribute = createAttribute(name, parameter, binderFactory, webRequest); } // 异常捕获处理 catch (BindException ex) { if (isBindExceptionRequired(parameter)) { // No BindingResult parameter -> fail with BindException throw ex; } // Otherwise, expose null/empty value and associated BindingResult if (parameter.getParameterType() == Optional.class) { attribute = Optional.empty(); } else { attribute = ex.getTarget(); } bindingResult = ex.getBindingResult(); } } // 还没有绑定结果,那要进行绑定 if (bindingResult == null) { // Bean property binding and validation; // skipped in case of binding failure on construction. // 创建一个绑定对象 WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name); // binder.getTarget() 就是你要绑定的对象 比如上边的 OrderDto 对象 if (binder.getTarget() != null) { // 这个就是判断那个标志,判断是否要进行参数绑定 if (!mavContainer.isBindingDisabled(name)) { // 进行参数绑定,这里不仅仅包含绑定,也包含参数类型转换!!! bindRequestParameters(binder, webRequest); } // 判断参数是否含有 Validated 校验注解 有的话调用校验 validateIfApplicable(binder, parameter); // 绑定异常处理 if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new BindException(binder.getBindingResult()); } } // Value type adaptation, also covering java.util.Optional // 会再一次进行类型转换判断,如果类型和方法参数定义的类型不一样会进行转换 if (!parameter.getParameterType().isInstance(attribute)) { attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter); } // 赋值绑定结果 bindingResult = binder.getBindingResult(); } // Add resolved attribute and BindingResult at the end of the model Map<String, Object> bindingResultModel = bindingResult.getModel(); mavContainer.removeAttributes(bindingResultModel); mavContainer.addAllAttributes(bindingResultModel); return attribute; }
大概分这几个步骤:
(1)获取参数名称
(2)创建参数类型对象比如上边的创建了一个 OrderDto 空对象
(3)创建当前属性的绑定对象进行参数值绑定以及转换 比如上边的 orderNo 参数,给 orderNo创建一个绑定对象,然后从请求中获取 orderNo的值,然后判断是否有转换器进行转换,最后将最终的值绑定到步骤(2)的空对象的orderNo 属性里
(4)二次判断值类型是否和方法参数类型定义是否一致,不一致进行转换器转换
其中步骤(2),ServletModelAttributeMethodProcessor 做了如下实现,也就是它首先会从请求中判断是否存在该属性的值,存在的话然后会判断有没有这样的转换器,有的话调用转换器进行对象创建,没有的话走父类的创建,通过反射获取构造器进行对象实例化创建。
// ServletModelAttributeMethodProcessor#createAttribute 实例话参数类型对象 protected final Object createAttribute(String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception { // 从请求中获取参数值 String value = getRequestValueForAttribute(attributeName, request); if (value != null) { // 不为空的话 调用createAttributeFromRequestValue 进行创建 Object attribute = createAttributeFromRequestValue(value, attributeName, parameter, binderFactory, request); // 不为空的话就用自己的,否则要用父类的创建 反射获取构造器进行创建 if (attribute != null) { return attribute; } } return super.createAttribute(attributeName, parameter, binderFactory, request); } // ServletModelAttributeMethodProcessor#createAttributeFromRequestValue protected Object createAttributeFromRequestValue(String sourceValue, String attributeName, MethodParameter parameter, WebDataBinderFactory binderFactory, NativeWebRequest request) throws Exception { // 创建参数绑定对象 DataBinder binder = binderFactory.createBinder(request, null, attributeName); // 获取转换服务 ConversionService conversionService = binder.getConversionService(); if (conversionService != null) { // 直接进行参数值 到 参数类型的转换 TypeDescriptor source = TypeDescriptor.valueOf(String.class); TypeDescriptor target = new TypeDescriptor(parameter); // 如果存在这种转换器才进行转换 否则直接返回空 if (conversionService.canConvert(source, target)) { return binder.convertIfNecessary(sourceValue, parameter.getParameterType(), parameter); } } return null; }
步骤(3)的话也是比较昼长,大家只要知道它是会获取转换器进行参数类型适配转换的,步骤(4)也是二次进行判断转换。
2.2 RequestResponseBodyMethodProcessor
RequestResponseBodyMethodProcessor 就是解析我们平时写的 @ResponseBody 的参数,比如:
@PostMapping("/receiveByRequestBody")
public OrderDto receiveByRequestBody(@RequestBody OrderDto orderDto) {
log.info("orderDto={}", orderDto);
return orderDto;
}
效果如下:

RequestResponseBodyMethodProcessor 继承了AbstractMessageConverterMethodArgumentResolver,我们看下类图:

看下他的解析:
// RequestResponseBodyMethodProcessor#resolveArgument public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { // 获取方法参数对象 parameter = parameter.nestedIfOptional(); // 根据消息转换器读取参数对象 Object arg = readWithMessageConverters(webRequest, parameter, parameter.getNestedGenericParameterType()); String name = Conventions.getVariableNameForParameter(parameter); if (binderFactory != null) { // 创建绑定对象 WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name); if (arg != null) { // 参数注解校验 validateIfApplicable(binder, parameter); // 校验有异常的话 直接抛出 if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new MethodArgumentNotValidException(parameter, binder.getBindingResult()); } } if (mavContainer != null) { mavContainer.addAttribute(BindingResult.MODEL_KEY_PREFIX + name, binder.getBindingResult()); } } return adaptArgumentIfNecessary(arg, parameter); }
其实主要就是根据消息转换器进行读取:
// RequestResponseBodyMethodProcessor#readWithMessageConverters // webRequest 请求对象 // parameter 方法参数对象 // paramType 参数类型比如上边的 OrderDto protected <T> Object readWithMessageConverters(NativeWebRequest webRequest, MethodParameter parameter, Type paramType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException { // 请求对象 HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); Assert.state(servletRequest != null, "No HttpServletRequest"); ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(servletRequest); // 调用父类的读取 Object arg = readWithMessageConverters(inputMessage, parameter, paramType); if (arg == null && checkRequired(parameter)) { throw new HttpMessageNotReadableException("Required request body is missing: " + parameter.getExecutable().toGenericString(), inputMessage); } return arg; } // AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters // inputMessage ServletServerHttpRequest // parameter MethodParameter 方法参数 // targetType 参数类型 比如 OrderDto类 protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableExcepti MediaType contentType; boolean noContentType = false; // 获取请求头里的媒体类型 try { contentType = inputMessage.getHeaders().getContentType(); } catch (InvalidMediaTypeException ex) { throw new HttpMediaTypeNotSupportedException(ex.getMessage()); } // 没有的话 默认为application/octet-stream if (contentType == null) { noContentType = true; contentType = MediaType.APPLICATION_OCTET_STREAM; } // 方法参数所在的类 比如某个 Controller 类 Class<?> contextClass = parameter.getContainingClass(); // 目标类 比如我们的 OrderDto 类 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 = null; try { message = new EmptyBodyCheckingHttpInputMessage(inputMessage); // 遍历消息转换器 json形式的默认的转换器是 MappingJackson2HttpMessageConverter 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); } finally { if (message != null && message.hasBody()) { closeStreamIfNecessary(message.getBody()); } } // 空值检查 if (body == NO_VALUE) { if (httpMethod == null || !SUPPORTED_METHODS.contains(httpMethod) || (noContentType && !message.hasBody())) { return null; } throw new HttpMediaTypeNotSupportedException(contentType, getSupportedMediaTypes(targetClass != null ? targetClass : Object.class)); } // 打印日志 MediaType selectedContentType = contentType; Object theBody = body; LogFormatUtils.traceDebug(logger, traceOn -> { String formatted = LogFormatUtils.formatValue(theBody, !traceOn); return "Read \"" + selectedContentType + "\" to [" + formatted + "]"; }); return body; }
最后其实就是 MappingJackson2HttpMessageConverter 这个消息转换器工作的,就跟我们平时使用 jackson 一样的:
objectMapper.readValue(inputStream, javaType);
2.3 ServletRequestMethodArgumentResolver
ServletRequestMethodArgumentResolver 支持填充的类型还是比较多的:
// ServletRequestMethodArgumentResolver#supportsParameter() public boolean supportsParameter(MethodParameter parameter) { Class<?> paramType = parameter.getParameterType(); return (WebRequest.class.isAssignableFrom(paramType) || ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType) || HttpSession.class.isAssignableFrom(paramType) || (pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) || (Principal.class.isAssignableFrom(paramType) && !parameter.hasParameterAnnotations()) || InputStream.class.isAssignableFrom(paramType) || Reader.class.isAssignableFrom(paramType) || HttpMethod.class == paramType || Locale.class == paramType || TimeZone.class == paramType || ZoneId.class == paramType); }
使用方式比如:
@GetMapping("/testServlet")
public void testServlet(HttpServletRequest request, HttpServletResponse response) {
log.info("request={}, response={}", request, response);
}
效果如下:

看下他的解析:
// ServletRequestMethodArgumentResolver#resolveArgument public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { // 参数类型 Class<?> paramType = parameter.getParameterType(); // 要填充 WebRequest 的话 // WebRequest / NativeWebRequest / ServletWebRequest if (WebRequest.class.isAssignableFrom(paramType)) { if (!paramType.isInstance(webRequest)) { throw new IllegalStateException( "Current request is not of type [" + paramType.getName() + "]: " + webRequest); } return webRequest; } // 填充 ServletRequest、MultipartRequest // ServletRequest / HttpServletRequest / MultipartRequest / MultipartHttpServletRequest if (ServletRequest.class.isAssignableFrom(paramType) || MultipartRequest.class.isAssignableFrom(paramType)) { return resolveNativeRequest(webRequest, paramType); } // 处理其他类型 // HttpServletRequest required for all further argument types return resolveArgument(paramType, resolveNativeRequest(webRequest, HttpServletRequest.class)); }
2.4 ServletResponseMethodArgumentResolver
ServletResponseMethodArgumentResolver 支持三种类型的参数,如下:
// ServletResponseMethodArgumentResolver#supportsParameter() public boolean supportsParameter(MethodParameter parameter) { Class<?> paramType = parameter.getParameterType(); return (ServletResponse.class.isAssignableFrom(paramType) || OutputStream.class.isAssignableFrom(paramType) || Writer.class.isAssignableFrom(paramType)); }
我们看下他的解析:
// ServletResponseMethodArgumentResolver#resolveArgument() public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { if (mavContainer != null) { mavContainer.setRequestHandled(true); } Class<?> paramType = parameter.getParameterType(); // ServletResponse, HttpServletResponse if (ServletResponse.class.isAssignableFrom(paramType)) { return resolveNativeResponse(webRequest, paramType); } // ServletResponse required for all further argument types return resolveArgument(paramType, resolveNativeResponse(webRequest, ServletResponse.class)); }
3 小结
好啦,本节我们主要看了四个解析器,有理解不对的地方还请指正哈。

浙公网安备 33010602011771号