精盡Spring MVC原始碼分析 - HandlerAdapter 元件(四)之 HandlerMethodReturnValueHandler
阿新 • • 發佈:2020-12-21
> 該系列文件是本人在學習 Spring MVC 的原始碼過程中總結下來的,可能對讀者不太友好,請結合我的原始碼註釋 [Spring MVC 原始碼分析 GitHub 地址](https://github.com/liu844869663/spring-framework) 進行閱讀
>
> Spring 版本:5.2.4.RELEASE
>
> 該系列其他文件請檢視:[**《精盡 Spring MVC 原始碼分析 - 文章導讀》**](https://www.cnblogs.com/lifullmoon/p/14123963.html)
## HandlerAdapter 元件
HandlerAdapter 元件,處理器的介面卡。因為處理器 `handler` 的型別是 Object 型別,需要有一個呼叫者來實現 `handler` 是怎麼被執行。Spring 中的處理器的實現多變,比如使用者的處理器可以實現 Controller 介面或者 HttpRequestHandler 介面,也可以用 `@RequestMapping` 註解將方法作為一個處理器等,這就導致 Spring MVC 無法直接執行這個處理器。所以這裡需要一個處理器介面卡,由它去執行處理器
由於 HandlerMapping 元件涉及到的內容較多,考慮到內容的排版,所以將這部分內容拆分成了五個模組,依次進行分析:
- [**《HandlerAdapter 元件(一)之 HandlerAdapter》**](https://www.cnblogs.com/lifullmoon/p/14137467.html)
- [**《HandlerAdapter 元件(二)之 ServletInvocableHandlerMethod》**](https://www.cnblogs.com/lifullmoon/p/14137483.html)
- [**《HandlerAdapter 元件(三)之 HandlerMethodArgumentResolver》**](https://www.cnblogs.com/lifullmoon/p/14137494.html)
- [**《HandlerAdapter 元件(四)之 HandlerMethodReturnValueHandler》**](https://www.cnblogs.com/lifullmoon/p/14137508.html)
- [**《HandlerAdapter 元件(五)之 HttpMessageConverter》**](https://www.cnblogs.com/lifullmoon/p/14137520.html)
## HandlerAdapter 元件(四)之 HandlerMethodReturnValueHandler
本文是接著[**《HandlerAdapter 元件(三)之 HandlerMethodArgumentResolver》**](https://www.cnblogs.com/lifullmoon/p/14137494.html)一文來分享 **HandlerMethodReturnValueHandler** 元件。在 `HandlerAdapter` 執行處理器的過程中,具體的執行過程交由 `ServletInvocableHandlerMethod` 物件來完成,其中需要先通過 `HandlerMethodArgumentResolver` 引數解析器從請求中解析出方法的入參,然後再通過反射機制呼叫對應的方法,獲取到執行結果後需要通過 **HandlerMethodReturnValueHandler** 結果處理器來進行處理。
### 回顧
先來回顧一下 `ServletInvocableHandlerMethod` 在哪裡呼叫返回值處理器的,可以回到 [**《HandlerAdapter 元件(二)之 ServletInvocableHandlerMethod》**](https://www.cnblogs.com/lifullmoon/p/14137483.html) 中 **ServletInvocableHandlerMethod** 小節下面的 `invokeAndHandle` 方法,如下:
```java
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// <1> 執行呼叫
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// <2> 設定響應狀態碼
setResponseStatus(webRequest);
// <3> 設定 ModelAndViewContainer 為請求已處理,返回,和 @ResponseStatus 註解相關
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
} else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
// <4> 設定 ModelAndViewContainer 為請求未處理
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// <5> 處理返回值
this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
} catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
```
- `<5>` 處呼叫 `returnValueHandlers` 對返回結果進行處理
- `returnValueHandlers` 為 HandlerMethodReturnValueHandlerComposite 組合物件,包含了許多的結果處理器
### HandlerMethodReturnValueHandler 介面
`org.springframework.web.method.support.HandlerMethodReturnValueHandler`,返回結果處理器
```java
public interface HandlerMethodReturnValueHandler {
/**
* 是否支援該型別
*/
boolean supportsReturnType(MethodParameter returnType);
/**
* 處理返回值
*/
void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
```
### 類圖
因為返回結果型別是多變的,所以會有許多的 HandlerMethodReturnValueHandler 的實現類,上圖僅列出了本文會分析的兩個實現類
### ModelAndViewContainer
`org.springframework.web.method.support.ModelAndViewContainer`,主要是作為 Model 和 View 的容器
#### 構造方法
```java
public class ModelAndViewContainer {
/**
* 是否在 redirect 重定向時,忽略 {@link #redirectModel}
*/
private boolean ignoreDefaultModelOnRedirect = false;
/**
* 檢視,Object 型別。
*
* 實際情況下,也可以是 String 型別的邏輯檢視
*/
@Nullable
private Object view;
/**
* 預設使用的 Model 。實際上是個 Map
*/
private final ModelMap defaultModel = new BindingAwareModelMap();
/**
* redirect 重定向的 Model ,在重定向時使用。
*/
@Nullable
private ModelMap redirectModel;
/**
* 處理器返回 redirect 檢視的標識
*/
private boolean redirectModelScenario = false;
/**
* Http 響應狀態
*/
@Nullable
private HttpStatus status;
private final Set noBinding = new HashSet<>(4);
private final Set bindingDisabled = new HashSet<>(4);
/**
* 用於設定 SessionAttribute 的標識
*/
private final SessionStatus sessionStatus = new SimpleSessionStatus();
/**
* 請求是否處理完的標識
*/
private boolean requestHandled = false;
}
```
#### getModel
`getModel()` 方法,獲得 Model 物件。程式碼如下:
```java
public ModelMap getModel() {
// 是否使用預設 Model
if (useDefaultModel()) {
return this.defaultModel;
}
else {
if (this.redirectModel == null) {
this.redirectModel = new ModelMap();
}
return this.redirectModel;
}
}
/**
* Whether to use the default model or the redirect model.
*/
private boolean useDefaultModel() {
return (!this.redirectModelScenario || (this.redirectModel == null && !this.ignoreDefaultModelOnRedirect));
}
```
- 從程式碼中,可以看出,有兩種情況下,使用 `defaultModel` 預設 Model :
- 情況一 `!this.redirectModelScenario` ,處理器返回 redirect 檢視的標識為 `false` 的時候,即不重定向
- 情況二 `this.redirectModel == null && !this.ignoreDefaultModelOnRedirect` ,`redirectModel` 重定向 Model 為**空**,並且 `ignoreDefaultModelOnRedirect` 為 `true` ,即忽略 `defaultModel`
- 那麼,問題就來了,redirectModelScenario 和 ignoreDefaultModelOnRedirect 什麼時候被改變?
- `redirectModelScenario` 屬性,在下文的 **ViewNameMethodReturnValueHandler**的**handleReturnValue**方法中會設定為`true`,詳情見下文
- `ignoreDefaultModelOnRedirect` 屬性,和 RequestMappingHandlerAdapter 的 `ignoreDefaultModelOnRedirect` 的屬性是一致的,預設為`false`
在 RequestMappingHandlerAdapter 的 `invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod)` 方法中,進行設定
- 另外,`org.springframework.ui.ModelMap` 是繼承 LinkedHashMap 類,並增加了部分常用方法,比較簡單
#### View 相關的方法
```java
public void setViewName(@Nullable String viewName) {
this.view = viewName;
}
@Nullable
public String getViewName() {
return (this.view instanceof String ? (String) this.view : null);
}
public void setView(@Nullable Object view) {
this.view = view;
}
@Nullable
public Object getView() {
return this.view;
}
public boolean isViewReference() {
return (this.view instanceof String);
}
```
#### requestHandled 屬性
請求是否處理完的標識
關於 `requestHandled` 的修改地方,實際在 Spring MVC 地方蠻多處都可以進行修改,例如:
- 在本文的開始處,`ServletInvocableHandlerMethod` 物件的 `invokeAndHandle` 方法中,會先設定為 `false`,表示請求還未處理,再交由 HandlerMethodReturnValueHandler 結果處理器去處理
- 在後文的 `RequestResponseBodyMethodProcessor` 的 `handleReturnValue` 會設定為 `true`
處理完結果後,接下來 `RequestMappingHandlerAdapter` 需要通過 `ModelAndViewContainer` 獲取 `ModelAndView` 物件,會用到 `requestHandled` 這個屬性
```java
// RequestMappingHandlerAdapter.java
@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
modelFactory.updateModel(webRequest, mavContainer);
// 情況一,如果 mavContainer 已處理,則返回“空”的 ModelAndView 物件。
if (mavContainer.isRequestHandled()) {
return null;
}
// 情況二,如果 mavContainer 未處理,則基於 `mavContainer` 生成 ModelAndView 物件
ModelMap model = mavContainer.getModel();
// 建立 ModelAndView 物件,並設定相關屬性
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
if (request != null) {
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
}
return mav;
}
```
看到沒,如果已處理,則返回的 `ModelAndView` 物件為 `null`
### HandlerMethodReturnValueHandlerComposite
`org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite`,實現 HandlerMethodReturnValueHandler 介面,複合的 HandlerMethodReturnValueHandler 實現類
#### 構造方法
```java
public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
/** HandlerMethodReturnValueHandler 陣列 */
private final List returnValueHandlers = new ArrayList<>();
}
```
在[**《HandlerAdapter 元件(一)之 HandlerAdapter》**](https://www.cnblogs.com/lifullmoon/p/14137467.html)的**RequestMappingHandlerAdapter**小節的 `getDefaultReturnValueHandlers` 方法中可以看到,預設的 `returnValueHandlers` 有哪些 HandlerMethodReturnValueHandler 實現類,注意這裡是有順序的新增哦
#### getReturnValueHandler
`getReturnValueHandler(MethodParameter returnType)` 方法,獲得方法返回值對應的 HandlerMethodReturnValueHandler 物件,方法如下:
```java
@Nullable
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
```
很簡單,遍歷所有的 HandlerMethodReturnValueHandler 實現類,如果支援這個返回結果,則直接返回
這裡為什麼不加快取呢?
#### supportsReturnType
`supportsReturnType(MethodParameter returnType)`方法,判斷是否支援該返回型別,方法如下:
```java
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return getReturnValueHandler(returnType) != null;
}
```
實際上就是呼叫 `getReturnValueHandler(MethodParameter returnType)` 方法,存在對應的 HandlerMethodReturnValueHandler 實現類表示支援
#### handleReturnValue
`handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest)`方法,處理返回值,方法如下:
```java
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 獲得 HandlerMethodReturnValueHandler 物件
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
if (handler == null) {
throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
}
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
```
這裡好奇的是沒有呼叫 `getReturnValueHandler(MethodParameter returnType)`方法獲取對應的 HandlerMethodReturnValueHandler 物件,而是呼叫 `selectHandler(Object value, MethodParameter returnType)` 方法,方法如下:
```java
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
// 判斷是否為非同步返回值
boolean isAsyncValue = isAsyncReturnValue(value, returnType);
// 遍歷 HandlerMethodReturnValueHandler 陣列,逐個判斷是否支援
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;
}
// 如果支援,則返回
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
private boolean isAsyncReturnValue(@Nullable Object value, MethodParameter returnType) {
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (handler instanceof AsyncHandlerMethodReturnValueHandler &&
((AsyncHandlerMethodReturnValueHandler) handler).isAsyncReturnValue(value, returnType)) {
return true;
}
}
return false;
}
```
在 `getReturnValueHandler(MethodParameter returnType)` 的基礎上,增加了**非同步**處理器 AsyncHandlerMethodReturnValueHandler 的判斷
### 【重點】RequestResponseBodyMethodProcessor
`org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor`,繼承 AbstractMessageConverterMethodProcessor 抽象類,處理方法引數添加了 `@RequestBody` 註解方法入參,或者處理方法添加了 `@ResponseBody` 註解的返回值。
因為前後端分離之後,後端基本是提供 Restful API ,所以 RequestResponseBodyMethodProcessor 成為了目前最常用的 HandlerMethodReturnValueHandler 實現類。
從圖中,我們也會發現,RequestResponseBodyMethodProcessor 也是 HandlerMethodArgumentResolver 的實現類。示例程式碼:
```java
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/walks")
public List walk(@RequestBody User user) {
List users = new ArrayList();
users.add(new User().setUsername("nihao"));
users.add(new User().setUsername("zaijian"));
return users;
}
}
```
雖然,`walks()` 方法的返回值沒新增 `@ResponseBody` 註解,但是 `@RestController` 註解,預設有 `@ResponseBody` 註解
#### 構造方法
```java
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
public RequestResponseBodyMethodProcessor(List> converters) {
super(converters);
}
public RequestResponseBodyMethodProcessor(List> converters,
@Nullable ContentNegotiationManager manager) {
super(converters, manager);
}
public RequestResponseBodyMethodProcessor(List> converters,
@Nullable List