SpringMVC常见组件之HandlerMethodReturnValueHandler解析-1 https://developer.aliyun.com/article/1382023
【8】ModelAttributeMethodProcessor
ModelAttributeMethodProcessor与子类ServletModelAttributeMethodProcessor主要用来处理标注了@ModelAttribute注解的方法,解析方法的参数并处理方法的返回结果。如果属性annotationNotRequired被设置为true,那么非简单类型的参数或返回结果将会被该处理器默认处理。
① supportsReturnType
处理标注了@ModelAttribute
注解的返回结果或者属性annotationNotRequired
为true并且返回结果类型为非简单类型。
@Override public boolean supportsReturnType(MethodParameter returnType) { return (returnType.hasMethodAnnotation(ModelAttribute.class) || (this.annotationNotRequired && !BeanUtils.isSimpleProperty(returnType.getParameterType()))); }
什么是简单类型?如下图所示(前面我们在SpringMVC常见组件之HandlerMethodArgumentResolver解提到过)。
② handleReturnValue
如下所示如果返回结果不为null,则根据返回结果值与类型获取name,然后放到mavContainer的model中。
@Override public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { if (returnValue != nul l) { String name = ModelFactory.getNameForReturnValue(returnValue, returnType); mavContainer.addAttribute(name, returnValue); } }
获取name的方法如下所示,如果有注解ModelAttribute并且注解value属性不为null,则使用注解的value属性。否则的话根据方法与所属类获取返回结果类型Class,进而生成一个name。
public static String getNameForReturnValue(@Nullable Object returnValue, MethodParameter returnType) { ModelAttribute ann = returnType.getMethodAnnotation(ModelAttribute.class); if (ann != null && StringUtils.hasText(ann.value())) { return ann.value(); } else { Method method = returnType.getMethod(); Assert.state(method != null, "No handler method"); Class<?> containingClass = returnType.getContainingClass(); Class<?> resolvedType = GenericTypeResolver.resolveReturnType(method, containingClass); return Conventions.getVariableNameForReturnType(method, resolvedType, returnValue); } }
【9】HttpHeadersReturnValueHandler
① supportsReturnType
处理返回结果类型是HttpHeaders的。
@Override public boolean supportsReturnType(MethodParameter returnType) { return HttpHeaders.class.isAssignableFrom(returnType.getParameterType()); }
② handleReturnValue
@Override @SuppressWarnings("resource") public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { mavContainer.setRequestHandled(true); Assert.state(returnValue instanceof HttpHeaders, "HttpHeaders expected"); HttpHeaders headers = (HttpHeaders) returnValue; if (!headers.isEmpty()) { HttpServletResponse servletResponse = webRequest.getNativeResponse(HttpServletResponse.class); Assert.state(servletResponse != null, "No HttpServletResponse"); ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(servletResponse); outputMessage.getHeaders().putAll(headers); outputMessage.getBody(); // flush headers } }
方法解释如下:
① 设置当前请求已经被处理mavContainer.setRequestHandled(true);
② 判断返回结果必须为HttpHeaders类型,否则抛出IllegalStateException异常;
③ 如果headers不为空,则获取ServletServerHttpResponse 然后将headers添加到响应头中;
④ 调用outputMessage.getBody();这里会进一步调用writeHeaders();,也就是所谓的flush headers。
【10】HttpEntityMethodProcessor
HttpEntityMethodProcessor继承自AbstractMessageConverterMethodProcessor,主要是解析HttpEntity和RequestEntity方法参数并处理HttpEntity类型和ResponseEntity类型的返回结果。
同ModelAndViewMethodReturnValueHandler一样,HttpEntityMethodProcessor应该被配置在某些处理器**前面(**支持标注了@ModelAttribute或@ResponseBody注解的方法的返回结果类型的),以免被覆盖。
① supportsReturnType
返回结果是HttpEntity类型并且不是RequestEntity子类型(RequestEntity是HttpEntity的子类)。
@Override public boolean supportsReturnType(MethodParameter returnType) { return (HttpEntity.class.isAssignableFrom(returnType.getParameterType()) && !RequestEntity.class.isAssignableFrom(returnType.getParameterType())); }
② handleReturnValue
@Override public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { // 设置请求已经被处理 mavContainer.setRequestHandled(true); // 如果返回结果为null,直接返回 if (returnValue == null) { return; } // 根据NativeWebRequest获取ServletServerHttpRequest ServletServerHttpRequest inputMessage = createInputMessage(webRequest); //根据NativeWebRequest获取ServletServerHttpResponse ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); //如果不是HttpEntity类型则抛出异常 Assert.isInstanceOf(HttpEntity.class, returnValue); HttpEntity<?> responseEntity = (HttpEntity<?>) returnValue; // 获取原始响应头 HttpHeaders outputHeaders = outputMessage.getHeaders(); // 获取返回结果的头信息 HttpHeaders entityHeaders = responseEntity.getHeaders(); // 如果返回结果头信息不为空,则将其放入原始响应头中。注意这里对VARY进行了特殊处理 if (!entityHeaders.isEmpty()) { entityHeaders.forEach((key, value) -> { if (HttpHeaders.VARY.equals(key) && outputHeaders.containsKey(HttpHeaders.VARY)) { List<String> values = getVaryRequestHeadersToAdd(outputHeaders, entityHeaders); if (!values.isEmpty()) { outputHeaders.setVary(values); } } else { outputHeaders.put(key, value); } }); } // 如果返回结果是ResponseEntity类型,根据200或者3XX进行不同处理 if (responseEntity instanceof ResponseEntity) { int returnStatus = ((ResponseEntity<?>) responseEntity).getStatusCodeValue(); outputMessage.getServletResponse().setStatus(returnStatus); if (returnStatus == 200) { HttpMethod method = inputMessage.getMethod(); if ((HttpMethod.GET.equals(method) || HttpMethod.HEAD.equals(method)) && isResourceNotModified(inputMessage, outputMessage)) { outputMessage.flush(); return; } } else if (returnStatus / 100 == 3) { String location = outputHeaders.getFirst("location"); if (location != null) { saveFlashAttributes(mavContainer, webRequest, location); } } } // Try even with null body. ResponseBodyAdvice could get involved. writeWithMessageConverters(responseEntity.getBody(), returnType, inputMessage, outputMessage); // Ensure headers are flushed even if no body was written. outputMessage.flush(); }
方法首先进行了前置处理,设置RequestHandled为true标明请求已经处理完毕。如果判断返回结果为null则直接返回。
下面两行代码很有意思,这里webRequest是NativeWebRequest类型,实现类如ServletRequestAttributes、FacesWebRequest等。ServletRequestAttributes与FacesWebRequest拥有属性HttpServletRequest request、HttpServletResponse response与HttpSession session等。
在其createInputMessage方法和createOutputMessage中首先获取HttpServletRequest 、HttpServletResponse 然后分别包装成ServletServerHttpRequest与ServletServerHttpResponse返回。所以千万不要别参数名字误导,以为其只是一个单纯的request。
ServletServerHttpRequest inputMessage = createInputMessage(webRequest); ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); protected ServletServerHttpRequest createInputMessage(NativeWebRequest webRequest) { HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); Assert.state(servletRequest != null, "No HttpServletRequest"); return new ServletServerHttpRequest(servletRequest); } protected ServletServerHttpResponse createOutputMessage(NativeWebRequest webRequest) { HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class); Assert.state(response != null, "No HttpServletResponse"); return new ServletServerHttpResponse(response); }
然后就是对entityHeaders进行了处理,基本就是把entityHeaders放入原始ServletServerHttpResponse的HttpHeaders中。注意,这里对HttpHeaders.VARY做了特殊处理。
如果返回结果是ResponseEntity类型,那么根据状态码是200或者3XX系列(重定向)进行不同处理。
如果status是200,且方法是(GET方法或HEAD方法)&&ResourceNotModified,那么会调用响应的flush方法(写入响应头、刷空缓冲区);
如果状态码为3XX,获取响应头中的location。如果location不为空,则进行“闪存属性”处理。简单来说就是重定向属性的更新。
writeWithMessageConverters这一步很关键,会进行message的转换,也就是所谓的格式化。将响应内容转换为需要的格式。
outputMessage.flush();会确保headers被flushed即时响应体为空。这里我们可以看下flush方法,如下所示其首先会处理header。然后判断当前body是否被使用,如果被使用则调用flushBuffer方法将缓冲区的内容刷到客户端。
@Override public void flush() throws IOException { writeHeaders(); if (this.bodyUsed) { this.servletResponse.flushBuffer(); } }
【11】RequestResponseBodyMethodProcessor
你所使用的@RequestBody注解或者@ResponseBody注解起作用就是该Processor在工作。该Processor会处理标注了@RequestBody的方法参数和标注了@ResponseBody注解的方法返回结果。其将会从请求中读入信息并将返回结果写入到响应体中,在这其中会使用HttpMessageConverter进行必要的格式转换。
如果使用@Valid注解,那么@RequestBody的方法参数同样被校验。如果配置了DefaultHandlerExceptionResolver,那么校验失败时会抛出状态码为400的异常。
① supportsReturnType
使用了@ResponseBody注解或者方法标注了@ResponseBody注解。
@Override public boolean supportsReturnType(MethodParameter returnType) { return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || returnType.hasMethodAnnotation(ResponseBody.class)); }
② handleReturnValue
@Override public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { mavContainer.setRequestHandled(true); ServletServerHttpRequest inputMessage = createInputMessage(webRequest); ServletServerHttpResponse outputMessage = createOutputMessage(webRequest); // Try even with null return value. ResponseBodyAdvice could get involved. writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); }
相比HttpEntityMethodProcessor,这里返回结果处理就简单多了。
① 设置请求被处理完毕;
② 获取ServletServerHttpRequest 和ServletServerHttpResponse
③ 进行信息格式转换,然后将转换后的message写入到响应体中,最后会调用outputMessage.getBody().flush();方法,将缓冲区的内容刷冲到客户端。
③ createInputMessage
我们看一下其ServletServerHttpRequest inputMessage = createInputMessage(webRequest);。如下所示其根据NativeWebRequest 获取到HttpServletRequest 然后包装为ServletServerHttpRequest实例对象返回。
protected ServletServerHttpRequest createInputMessage(NativeWebRequest webRequest) { HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class); Assert.state(servletRequest != null, "No HttpServletRequest"); return new ServletServerHttpRequest(servletRequest); }
如下图所示,ServletServerHttpRequest
实现了ServerHttpRequest
接口(后者继承了HttpRequest, HttpInputMessage接口)
我们再顺便看一下这几个接口继承示意与各自提供的方法:
public interface HttpMessage { HttpHeaders getHeaders(); } public interface HttpInputMessage extends HttpMessage { InputStream getBody() throws IOException; } public interface HttpRequest extends HttpMessage { @Nullable default HttpMethod getMethod() { return HttpMethod.resolve(getMethodValue()); } String getMethodValue(); URI getURI(); } public interface ServerHttpRequest extends HttpRequest, HttpInputMessage { @Nullable Principal getPrincipal(); InetSocketAddress getLocalAddress(); InetSocketAddress getRemoteAddress(); ServerHttpAsyncRequestControl getAsyncRequestControl(ServerHttpResponse response); }
④ createOutputMessage(webRequest)
我们继续看一下ServletServerHttpResponse outputMessage = createOutputMessage(webRequest);,放入如下所示根据NativeWebRequest 拿到HttpServletResponse (我们经常使用的),然后作为构造函数入参实例化一个ServletServerHttpResponse对象。
protected ServletServerHttpResponse createOutputMessage(NativeWebRequest webRequest) { HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class); Assert.state(response != null, "No HttpServletResponse"); return new ServletServerHttpResponse(response); }
我们再顺便看一下这几个接口继承示意与各自提供的方法:
public interface HttpMessage { HttpHeaders getHeaders(); } public interface HttpOutputMessage extends HttpMessage { OutputStream getBody() throws IOException; } public interface ServerHttpResponse extends HttpOutputMessage, Flushable, Closeable { void setStatusCode(HttpStatus status); @Override void flush() throws IOException; @Override void close(); }
⑤ writeWithMessageConverters
HttpEntityMethodProcessor和RequestResponseBodyMethodProcessor都是AbstractMessageConverterMethodProcessor的子类,其继承自AbstractMessageConverterMethodArgumentResolver,提供了能力-使用HttpMessageConverter处理方法返回结果,将转换后的结果写入到response。
这里我们先看一下writeWithMessageConverters
方法执行前各个参数实例。
我们展开看一下MethodParameter returnType
:
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException { Object body; Class<?> valueType; Type targetType; // 如果返回结果是String if (value instanceof CharSequence) { body = value.toString(); valueType = String.class; targetType = String.class; } else {// 返回结果不是string body = value; // 获取值类型 valueType = getReturnValueType(body, returnType); // 获取返回结果目标类型 targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass()); } // 如果返回结果类型是Resource,但不是InputStreamResource if (isResourceType(value, returnType)) { outputMessage.getHeaders().set(HttpHeaders.ACCEPT_RANGES, "bytes"); if (value != null && inputMessage.getHeaders().getFirst(HttpHeaders.RANGE) != null && outputMessage.getServletResponse().getStatus() == 200) { Resource resource = (Resource) value; try { List<HttpRange> httpRanges = inputMessage.getHeaders().getRange(); outputMessage.getServletResponse().setStatus(HttpStatus.PARTIAL_CONTENT.value()); body = HttpRange.toResourceRegions(httpRanges, resource); valueType = body.getClass(); targetType = RESOURCE_REGION_LIST_TYPE; } catch (IllegalArgumentException ex) { outputMessage.getHeaders().set(HttpHeaders.CONTENT_RANGE, "bytes */" + resource.contentLength()); outputMessage.getServletResponse().setStatus(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE.value()); } } } MediaType selectedMediaType = null; // 获取响应的contentType MediaType contentType = outputMessage.getHeaders().getContentType(); // 判断contentType是不是具体的--即不带通配符* boolean isContentTypePreset = contentType != null && contentType.isConcrete(); if (isContentTypePreset) { if (logger.isDebugEnabled()) { logger.debug("Found 'Content-Type:" + contentType + "' in response"); } // 如果返回结果是具体的,则直接赋予selectedMediaType selectedMediaType = contentType; } else { // 否则就要根据acceptableTypes 与producibleTypes 选择一个合适的selectedMediaType HttpServletRequest request = inputMessage.getServletRequest(); // 通过contentNegotiationManager解析request获取mediaType,其实就是根据HttpHeaders.ACCEPT List<MediaType> acceptableTypes = getAcceptableMediaTypes(request); // 1.从request中获取HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE属性对应的值; // 2.获取能够对当前返回结果进行写操作的converter支持的mediaType List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType); if (body != null && producibleTypes.isEmpty()) { throw new HttpMessageNotWritableException( "No converter found for return value of type: " + valueType); } List<MediaType> mediaTypesToUse = new ArrayList<>(); for (MediaType requestedType : acceptableTypes) { for (MediaType producibleType : producibleTypes) { if (requestedType.isCompatibleWith(producibleType)) { mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType)); } } } // 没有找到能够使用的mediaTypes,就抛出异常HttpMediaTypeNotAcceptableException if (mediaTypesToUse.isEmpty()) { if (body != null) { throw new HttpMediaTypeNotAcceptableException(producibleTypes); } if (logger.isDebugEnabled()) { logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes); } return; } //对mediaTypesToUse进行排序 具体的在前 MediaType.sortBySpecificityAndQuality(mediaTypesToUse); for (MediaType mediaType : mediaTypesToUse) { if (mediaType.isConcrete()) { selectedMediaType = mediaType; break; } else if (mediaType.isPresentIn(ALL_APPLICATION_MEDIA_TYPES)) { selectedMediaType = MediaType.APPLICATION_OCTET_STREAM; break; } } if (logger.isDebugEnabled()) { logger.debug("Using '" + selectedMediaType + "', given " + acceptableTypes + " and supported " + producibleTypes); } } // 如果selectedMediaTypebuw if (selectedMediaType != null) { // 移除掉mediaType中的q selectedMediaType = selectedMediaType.removeQualityValue(); // 遍历messageConverters 找到一个合适的HttpMessageConverter for (HttpMessageConverter<?> converter : this.messageConverters) { // 判断当前converter是不是GenericHttpMessageConverter类型 GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null); if (genericConverter != null ? ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) : converter.canWrite(valueType, selectedMediaType)) { // 在向outputmessage写入body前,获取adviceBean,对响应结果进行处理 body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) converter.getClass(), inputMessage, outputMessage); if (body != null) { Object theBody = body; LogFormatUtils.traceDebug(logger, traceOn -> "Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]"); // 尝试添加HttpHeaders.CONTENT_DISPOSITION addContentDispositionHeader(inputMessage, outputMessage); if (genericConverter != null) { // 使用genericConverter 处理body然后写入到outputmessage genericConverter.write(body, targetType, selectedMediaType, outputMessage); } else { // 使用HttpMessageConverter处理body然后写入到outputmessage ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage); } } else { if (logger.isDebugEnabled()) { logger.debug("Nothing to write: null body"); } } return; } } } // 这里只会抛出异常 if (body != null) { Set<MediaType> producibleMediaTypes = (Set<MediaType>) inputMessage.getServletRequest() .getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE); if (isContentTypePreset || !CollectionUtils.isEmpty(producibleMediaTypes)) { throw new HttpMessageNotWritableException( "No converter for [" + valueType + "] with preset Content-Type '" + contentType + "'"); } throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes); } }
this.messageConverters
如下所示:
下面这个判断很有意思,如果genericConverter 不为null,则判断其是否能够进行写操作,否则判断类型转换前的converter是否能够进行写操作。如果都为false,则直接进行下个循环。默认情况下,这里我们会找到MappingJackson2HttpMessageConverter进行处理。
if (genericConverter != null ? ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) : converter.canWrite(valueType, selectedMediaType))
producibleTypes
与排序后的mediaTypesToUse
:
GenericHttpMessageConverter
的子类有如下所示:
如下图所示,当writeWithMessageConverters方法中下面代码执行完,客户端就会拿到响应结果。此时整个请求流程对springmvc来说还未结束!!!
if (genericConverter != null) { genericConverter.write(body, targetType, selectedMediaType, outputMessage); } else { ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage); }
【12ModelAndViewResolverMethodReturnValueHandler
这个处理器比较有意思,其supportsReturnType仅仅只是返回true。也就意味着,这是最后的保留手段。所以该处理器是配置在所有处理器的后面。
返回值可以使用ModelAndViewResolver处理。如果是非简单类型,则可以将其视为模型属性(model attribute使用modelAttributeProcessor处理)。如果这两种方法都不成功(本质上是简单类型,而不是字符串),则会引发UnsupportdOperationException。
需要注意的是,ModelAndViewResolver只是一个接口,目前并没有实现类。所以你可以实现ModelAndViewResolver将其作为一个HandlerMethodReturnValueHandler如下所示:其拥有两个属性mavResolvers
和modelAttributeProcessor
@Nullable private final List<ModelAndViewResolver> mavResolvers; private final ModelAttributeMethodProcessor modelAttributeProcessor = new ServletModelAttributeMethodProcessor(true);
@Override public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { if (this.mavResolvers != null) { for (ModelAndViewResolver mavResolver : this.mavResolvers) { Class<?> handlerType = returnType.getContainingClass(); Method method = returnType.getMethod(); Assert.state(method != null, "No handler method"); ExtendedModelMap model = (ExtendedModelMap) mavContainer.getModel(); // 解析得到MAV ModelAndView mav = mavResolver.resolveModelAndView(method, handlerType, returnValue, model, webRequest); if (mav != ModelAndViewResolver.UNRESOLVED) { //将属性放入mavContainer mavContainer.addAllAttributes(mav.getModel()); //为mavContainer设置viewName mavContainer.setViewName(mav.getViewName()); if (!mav.isReference()) { //设置view mavContainer.setView(mav.getView()); } return; } } } // No suitable ModelAndViewResolver... if (this.modelAttributeProcessor.supportsReturnType(returnType)) { this.modelAttributeProcessor.handleReturnValue(returnValue, returnType, mavContainer, webRequest); } else { throw new UnsupportedOperationException("Unexpected return type: " + returnType.getParameterType().getName() + " in method: " + returnType.getMethod()); } }
方法如上所示,先尝试使用ModelAndViewResolver 进行处理,然后使用modelAttributeProcessor进行处理。默认ModelAndViewResolver没有实现类,所以通常这里是将动作委派给具体的modelAttributeProcessor进行处理。
如果你想实现一个ModelAndViewResolver,那么考虑本文中【3】、【4】、【5】、【6】、【7】、【8】的实现。