在前面我们分析SpringMVC常见组件之HandlerAdapter分析中提到过如下过程:
RequestMappingHandlerAdapter.invokeAndHandle(webRequest, mavContainer);
--ServletInvocableHandlerMethod.invokeAndHandle(webRequest, mavContainer);
---`Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);`
---this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
其中很重要的一步就是在HandlerMethodReturnValueHandlerComposite
中解析方法返回结果,方法源码如下所示:
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
看到这里是不是就联想到了SpringMVC常见组件之HandlerMethodArgumentResolver解析中的HandlerMethodArgumentResolverComposite
?没错,都是策略接口,应用了组合模式和中介者模式,将动作
委派给具体的handler处理。
【1】HandlerMethodReturnValueHandler
方法返回结果处理器,其是一个策略接口,提供了两个方法让子类实现:supportsReturnType
用来判断当前返回结果处理器是否能够处理返回结果,handleReturnValue
方法用来处理返回结果。
public interface HandlerMethodReturnValueHandler {
// 当前handler是否能够处理 MethodParameter returnType
boolean supportsReturnType(MethodParameter returnType);
//向model添加数据并设置view或者设置响应已经处理
void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
我们再来看一下其家族树图示
如下所示,有20个类,值得一提的是某些处理器还实现了参数解析器的接口,如ModelMethodProcessor。
- ViewNameMethodReturnValueHandler
- MapMethodProcessor
- ViewMethodReturnValueHandler
- StreamingResponseBodyReturnValueHandler
- DeferredResultMethodReturnValueHandler
- HandlerMethodReturnValueHandlerComposite
- HttpHeadersReturnValueHandler
- CallableMethodReturnValueHandler
- ModelMethodProcessor
- ModelAttributeMethodProcessor
- ServletModelAttributeMethodProcessor
- ResponseBodyEmitterReturnValueHandler
- ModelAndViewMethodReturnValueHandler
- ModelAndViewResolverMethodReturnValueHandler
- AbstractMessageConverterMethodProcessor
- RequestResponseBodyMethodProcessor
- HttpEntityMethodProcessor
- AsyncHandlerMethodReturnValueHandler
- AsyncTaskMethodReturnValueHandler
处理器与返回类型表格
如下表格中“是否解析参数”,也就是说其同时实现了HandlerMethodArgumentResolver接口,可以解析参数。
处理器 | 是否解析参数 | 类型 |
---|---|---|
ModelAndViewMethodReturnValueHandler | 否 | ModelAndView |
ModelAndViewResolverMethodReturnValueHandler | 否 | 直接返回true |
ViewNameMethodReturnValueHandler | 否 | Void String CharSequence |
ViewMethodReturnValueHandler | 否 | View |
MapMethodProcessor | 是 | Map |
ModelMethodProcessor | 是 | Model |
ModelAttributeMethodProcessor | 是 | 标注了@ModelAttribute 的方法 |
RequestResponseBodyMethodProcessor | 是 | 标注了@ResponseBody 的方法 |
HttpEntityMethodProcessor | 是 | HttpEntity ResponseEntity |
ResponseBodyEmitterReturnValueHandler | 否 | ResponseBodyEmitter |
StreamingResponseBodyReturnValueHandler | 否 | StreamingResponseBodyResponseEntity<StreamingResponseBody> |
HttpHeadersReturnValueHandler | 否 | HttpHeaders |
CallableMethodReturnValueHandler | 否 | Callable |
DeferredResultMethodReturnValueHandler | 否 | DeferredResult ListenableFuture CompletionStage |
AsyncTaskMethodReturnValueHandler | 否 | WebAsyncTask |
HandlerMethodReturnValueHandlerComposite | 否 | 本身将任务分派给具体的处理器 |
【2】HandlerMethodReturnValueHandlerComposite
通过将处理动作委派给内部注册的一系列HandlerMethodReturnValueHandler来实现功能。其内部有个常量集合如下:
private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<>();
其首先应用了组合模式,无论ServletInvocableHandlerMethod
调用HandlerMethodReturnValueHandlerComposite
还是单个具体的HandlerMethodReturnValueHandler
,其行为都是一致的。
什么行为?第一是判断是否支持当前返回类型也就是supportsReturnType
方法;第二就是处理返回结果的方法handleReturnValue
。
其次应用了委派/策略
模式,InvocableHandlerMethod
在处理返回结果的时候根本不知道也不关心具体的HandlerMethodReturnValueHandler
是谁,其根据supportsReturnType
方法从returnValueHandlers
中筛选一个合适的处理器进行结果处理。
① supportsReturnType
如下所示,HandlerMethodReturnValueHandlerComposite
从returnValueHandlers
中找到一个支持当前返回类型的handler
,然后返回该HandlerMethodReturnValueHandler
。也就是将supportsReturnType
的动作委派给了returnValueHandlers
中的一个个具体的处理器。
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return getReturnValueHandler(returnType) != null;
}
@Nullable
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
② handleReturnValue
如下所示,HandlerMethodReturnValueHandlerComposite
首先从returnValueHandlers
拿到一个合适的HandlerMethodReturnValueHandler
然后使用该处理器进行返回结果处理。如果没有找到合适的HandlerMethodReturnValueHandler
,将会抛出异常。
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
// 将动作转发给具体的handler
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
// 找到一个合适的HandlerMethodReturnValueHandler
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
【3】ModelAndViewMethodReturnValueHandler
经典的视图处理器,处理返回结果类型为ModelAndView
的值。该处理器将会把ModelAndView
中的View信息和Model信息拷贝到ModelAndViewContainer
中。如果返回结果为null,那么将会设置ModelAndViewContainer
的RequestHandled
标志位true表名请求已经被直接处理完毕。
返回结果是ModelAndView
类型的是有一种固定的用途,因此ModelAndViewMethodReturnValueHandler
应该被配置在某些处理器
前面(支持标注了@ModelAttribute
或@ResponseBody
注解的方法的返回结果类型的),以免被覆盖。
① supportsReturnType
异常简单,如下所示判断返回结果类型是否为ModelAndView。
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
}
② handleReturnValue
代码如下所示,首先判断返回值是否为空,如果为空则设置请求处理标志位为true表示当前请求已经被处理,返回返回。
接下来根据mav
中的view
是否是String
进行不同的处理。这里需要注意的是进行了是否为重定向判断,如果是重定向如redirect:/user/list
,那么将会设置RedirectModelScenario
为true。
最后设置status
并将mav
中的model
属性都放到了mavContainer
中。
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 判断是否为空
if (returnValue == null) {
mavContainer.setRequestHandled(true);
return;
}
ModelAndView mav = (ModelAndView) returnValue;
// 判断当前mav中的view是否为String
if (mav.isReference()) {
String viewName = mav.getViewName();
mavContainer.setViewName(viewName);
if (viewName != null && isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
else {
View view = mav.getView();
mavContainer.setView(view);
if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {
mavContainer.setRedirectModelScenario(true);
}
}
//设置status
mavContainer.setStatus(mav.getStatus());
// 放入model数据
mavContainer.addAllAttributes(mav.getModel());
}
【4】ModelMethodProcessor
其是一个参数解析器&&返回结果处理器,解析Model类型的参数并处理Model类型的返回结果。
同ModelAndViewMethodReturnValueHandler
一样,ModelMethodProcessor
应该被配置在某些处理器
前面(支持标注了@ModelAttribute
或@ResponseBody
注解的方法的返回结果类型的),以免被覆盖。
① supportsReturnType
判断方法如下所示,返回当前类型是否是Model。
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return Model.class.isAssignableFrom(returnType.getParameterType());
}
② handleReturnValue
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue == null) {
return;
}
else if (returnValue instanceof Model) {
mavContainer.addAllAttributes(((Model) returnValue).asMap());
}
else {
// should not happen
throw new UnsupportedOperationException("Unexpected return type [" +
returnType.getParameterType().getName() + "] in method: " + returnType.getMethod());
}
}
代码解释如下:
- ① 如果returnValue是null,则直接返回;
- ② 如果返回值是Model类型,则将model里面数据放入mavContainer;
- ③ 否则,抛出UnsupportedOperationException异常
【5】MapMethodProcessor
解析Map类型参数并能够处理Map类型的返回结果。
由于加了注解@ModelAttribute或者@ResponseBody方法也可能返回Map类型,因为在5.2版本后,这个解析器不处理标注了注解的方法参数。
① supportsReturnType
如下所示,判断返回类型是否为Map。
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return Map.class.isAssignableFrom(returnType.getParameterType());
}
② handleReturnValue
方法如下所示,如果返回类型是map,则将map放入mavContainer中。否则如果返回结果不为null,则抛出UnsupportedOperationException异常。
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue instanceof Map){
mavContainer.addAllAttributes((Map) returnValue);
}
else if (returnValue != null) {
// should not happen
throw new UnsupportedOperationException("Unexpected return type [" +
returnType.getParameterType().getName() + "] in method: " + returnType.getMethod());
}
}
【6】ViewMethodReturnValueHandler
与视图相关,处理返回结果类型为View的。如果返回值为null,则由配置的RequestToViewNameTranslator
按照约定选择视图名称。
同ModelAndViewMethodReturnValueHandler
一样,ViewMethodReturnValueHandler
应该被配置在某些处理器
**前面(**支持标注了@ModelAttribute
或@ResponseBody
注解的方法的返回结果类型的),以免被覆盖。
① supportsReturnType
如下所示,ViewMethodReturnValueHandler只处理返回结果类型为View的。
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return View.class.isAssignableFrom(returnType.getParameterType());
}
② handleReturnValue
在处理返回结果的时候,会判断返回结果是否为View类型,然后设置为mavContainer中的View引用。如果其是RedirectView则设置重定向模型场景标志为true,表示当前时重定向请求。如果其不是View类型并且结果不为null,则抛出UnsupportedOperationException
异常。
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue instanceof View) {
View view = (View) returnValue;
mavContainer.setView(view);
if (view instanceof SmartView && ((SmartView) view).isRedirectView()) {
mavContainer.setRedirectModelScenario(true);
}
}
else if (returnValue != null) {
// should not happen
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}
【7】ViewNameMethodReturnValueHandler
与视图相关,处理返回结果为void、String以及基本的CharSequence。如果返回值为null,则由配置的RequestToViewNameTranslator
按照约定选择视图名称。需要注意的是,ViewNameMethodReturnValueHandler应该被配置在
某些处理器**后面(**支持标注了
@ModelAttribute或
@ResponseBody`注解的方法的返回结果类型的),以免覆盖。
① supportsReturnType
支持void类型或者CharSequence类型如String、StringBuilder。
@Override
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> paramType = returnType.getParameterType();
return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
}
② handleReturnValue
如果返回结果不是CharSequence类型并且不为null,则直接抛出异常。如果返回结果是CharSequence类型,则设置mavContainer的viewName并尝试判断是否为重定向view而设置RedirectModelScenario为true。
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue instanceof CharSequence) {
String viewName = returnValue.toString();
mavContainer.setViewName(viewName);
if (isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
else if (returnValue != null) {
// should not happen
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}
【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);
}
【12】ModelAndViewResolverMethodReturnValueHandler
这个处理器比较有意思,其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】的实现。