1. HandlerMethodReturnValueHandler 概述
HandlerMethodReturnValueHandler = HandlerMethod(@RequestMapping的處理器) + returnValue(返回值) + Handler(處理器), 其實就是 HandlerMethod 返滬值的處理器, 將返回值以特定的格式輸出到client端, 或?qū)?View 相關(guān)的信息設(shè)置到 ModelAndViewContainer 里面, 以便于視圖的渲染, 其接口如下:
// HandlerMethod 的返回值處理器
public interface HandlerMethodReturnValueHandler {
// 判斷當(dāng)前處理器是否支持 返回值
boolean supportsReturnType(MethodParameter returnType);
// 處理 HandlerMethod 的返回值
void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
基于這個接口實現(xiàn)的處理器主要是如下幾類:
1. 返回值通常是View類型或 ViewName 或 Map 類型, 通常都是設(shè)置到 ModelAndViewContainer 里面
2. 支持返回值是 DeferredResult, ListenableFuture, CompletionStage 類型, 最后轉(zhuǎn)為對應(yīng)的適配器, 交給WebAsyncManager 進行運行
3. 根據(jù) ContentType 的類型將數(shù)據(jù)通過 HttpMessageConverter 轉(zhuǎn)換成對應(yīng)的數(shù)據(jù)格式, 最后輸出到遠(yuǎn)端
2. 處理 View 或 String 類型的HandlerMethodReturnValueHandler
1. ViewMethodReturnValueHandler
針對 View 及其子類的返回值處理器, 主要還是將 View 設(shè)置到 ModelAndViewContainer
2. ViewNameMethodReturnValueHandler
支持返回值為 CharSequence 類型, 設(shè)置 ModelAndViewContainer.setViewName
3. StreamingResponseBodyReturnValueHandler
支持 StreamingResponseBody 類型的返回值, 最后將請求包裝成 Callable, 通過 Spring 的異步處理器 SimpleAsyncTaskExecutor 執(zhí)行 StreamingResponseBody 將數(shù)據(jù)流寫到遠(yuǎn)端
4. ModelMethodProcessor
針對 Model 及其子類的返回值處理器, 主要還是將 ModelAndView 中的 model 設(shè)置到 ModelAndViewContainer
5. ModelAttributeMethodProcessor
針對被 @ModelAttribute 修飾的返回值, 最后將數(shù)據(jù)寫入到 ModelAndViewContainer 中
6. ModelAndViewResolverMethodReturnValueHandler
通過 ModelAndViewResolver 將數(shù)據(jù)解析成 View, Model, 最后設(shè)置到 ModelAndViewContainer 里面
7. ModelAndViewMethodReturnValueHandler
針對 ModelAndView 及其子類的返回值處理器, 主要還是將 ModelAndView 中的 status, model 設(shè)置到 ModelAndViewContainer
8. MapMethodProcessor
支持返回值為 Map, 并將結(jié)果設(shè)置到 ModelAndViewContainer
9. HttpHeadersReturnValueHandler
針對 HttpHeaders 類型的返回值, 最后設(shè)置到 Response 的 Header 中, 輸出到遠(yuǎn)端
上面幾個 ReturnValueHandler 基本上就是將數(shù)據(jù)設(shè)置到 ModelAndViewContainer 里面
3. 處理異步返回值的 HandlerMethodReturnValueHandler
這一類接口都是 AsyncHandlerMethodReturnValueHandler 的子類, 主要如下:
1. AsyncTaskMethodReturnValueHandler
支持返回值是 WebAsyncTask 類型, 并通過 WebAsyncManager 中的 SimpleAsyncTaskExecutor 來進行處理
2. CallableMethodReturnValueHandler
支持返回值是 Callable 類型, 并通過 WebAsyncManager 中的 SimpleAsyncTaskExecutor 來進行處理
3. CompletionStageReturnValueHandler
支持返回值是 CompletionStage 類型, 并通過 WebAsyncManager 中的 SimpleAsyncTaskExecutor 來進行處理
4. DeferredResultMethodReturnValueHandler
這個類適配 DeferredResult, ListenableFuture, CompletionStage 類型的返回值, 最后通過 WebAsyncManager 來完成任務(wù)的調(diào)用
5. ListenableFutureReturnValueHandler
支持返回值是 ListenableFuture 類型, 并通過 WebAsyncManager 中的 SimpleAsyncTaskExecutor 來進行處理
4. 支持格式轉(zhuǎn)換的 HandlerMethodReturnValueHandler
這些 ReturnValueHandler 都是AbstractMessageConverterMethodProcessor的子類, 對應(yīng)實現(xiàn)的主邏輯也在 AbstractMessageConverterMethodProcessor 中
1. 獲取 request 對應(yīng)的 MediaType(PS: ContentNegotiationManager 進行獲取 Request 所能支持的 MediaType)
2. 返回 HttpMessageConverter 所支持的 MediaType
3. 若 producibleMediaType 是空 且 返回值有數(shù)據(jù), 直接拋出異常
4. 找出 producibleMediaType 與 AcceptMediaType 都兼容的 MediaType
5. 循環(huán)遍歷 HttpMessageConverter, 進行返回值的轉(zhuǎn)換處理 下面進行選擇 converter 時, 加入 MediaType 作為考慮
6. 通過 ResponseBodyAdvice 在將數(shù)據(jù)寫入輸出流之前進行一些增強操作
7. 對 Http 請求中的 header 進行一些配置信息進行處理
8. HttpMessageConverter 進行處理, 將數(shù)據(jù)寫入 outputStream, 并且寫到遠(yuǎn)端
主流程的代碼如下:
protected <T> void writeWithMessageConverters(T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
Object outputValue; // 返回值內(nèi)容
Class<?> valueType; // 返回值類型
Type declaredType;
if (value instanceof CharSequence) { // 若 value 是 Char
outputValue = value.toString(); // 設(shè)置 outputValue
valueType = String.class; // 設(shè)置 返回值的類型
declaredType = String.class;
} else {
outputValue = value; // 設(shè)置返回值
valueType = getReturnValueType(outputValue, returnType); // 獲取 返回值的類型
declaredType = getGenericType(returnType); // 返回值類型
}
HttpServletRequest request = inputMessage.getServletRequest();
// 獲取 request 對應(yīng)的 MediaType(PS: ContentNegotiationManager 進行獲取 Request 所能支持的 MediaType)
List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(request);
// 返回 HttpMessageConverter 所支持的 MediaType
List<MediaType> producibleMediaTypes = getProducibleMediaTypes(request, valueType, declaredType);
// 若 producibleMediaType 是空 且 返回值有數(shù)據(jù), 直接拋出異常
if (outputValue != null && producibleMediaTypes.isEmpty()) throw new IllegalArgumentException("No converter found for return value of type: " + valueType);
Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();
// 找出兩端都兼容的 MediaType
for (MediaType requestedType : requestedMediaTypes) {
// getMostSpecificMediaType 找出 兩個 MediaType 中更加適合 producible
for (MediaType producibleType : producibleMediaTypes) if (requestedType.isCompatibleWith(producibleType)) compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
}
if (compatibleMediaTypes.isEmpty()) {
// 未出現(xiàn)兩者都兼容的 MediaType, 則直接拋出異常
if (outputValue != null) throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
return;
}
List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes);
// 將所有的 MediaType 進行排序
MediaType.sortBySpecificityAndQuality(mediaTypes);
MediaType selectedMediaType = null;
// 篩選出其中一個 mediaType
for (MediaType mediaType : mediaTypes) {
if (mediaType.isConcrete()) {
selectedMediaType = mediaType;
break;
} else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
selectedMediaType = MediaType.APPLICATION_OCTET_STREAM; // 設(shè)置 application/octet-stream
break;
}
}
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
// 循環(huán)遍歷 HttpMessageConverter, 進行返回值的轉(zhuǎn)換處理 下面進行選擇 converter 時, 加入 MediaType 作為考慮
for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
// 下面分類 HttpMessageConverter 進行分開處理 <-- 先是支持 Type 的 HttpMessageConverter
if (messageConverter instanceof GenericHttpMessageConverter) {
if (((GenericHttpMessageConverter) messageConverter).canWrite(declaredType, valueType, selectedMediaType)) {
// 通過 ResponseBodyAdvice 在將數(shù)據(jù)寫入輸出流之前進行一些增強操作
outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(), inputMessage, outputMessage);
if (outputValue != null) {
// 對 Http 請求中的 header 進行一些處理
addContentDispositionHeader(inputMessage, outputMessage);
// HttpMessageConverter 進行處理, 將數(shù)據(jù)寫入 outputStream, 并且寫到遠(yuǎn)端
((GenericHttpMessageConverter) messageConverter).write(outputValue, declaredType, selectedMediaType, outputMessage);
logger.info("Written [" + outputValue + "] as \"" + selectedMediaType + "\" using [" + messageConverter + "]");
}
return;
}
}
// 普通 HttpMessageConverter 處理
else if (messageConverter.canWrite(valueType, selectedMediaType)) {
// 通過 ResponseBodyAdvice 在將數(shù)據(jù)寫入輸出流之前進行一些增強操作
outputValue = (T) getAdvice().beforeBodyWrite(outputValue, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(), inputMessage, outputMessage);
if (outputValue != null) {
// 對 Http 請求中的 header 進行一些處理
addContentDispositionHeader(inputMessage, outputMessage);
// HttpMessageConverter 進行處理, 將數(shù)據(jù)寫入 outputStream, 并且寫到遠(yuǎn)端
((HttpMessageConverter) messageConverter).write(outputValue, selectedMediaType, outputMessage);
logger.info("Written [" + outputValue + "] as \"" + selectedMediaType + "\" using [" + messageConverter + "]");
}
return;
}
}
}
if (outputValue != null) throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
}
而子類 HttpEntityMethodProcessor, RequestResponseBodyMethodProcessor, 先準(zhǔn)備數(shù)據(jù), 然后使用父類的writeWithMessageConverters 方法, 將數(shù)據(jù)寫入數(shù)據(jù)流中, 最后在刷到遠(yuǎn)端!
5. HandlerMethodReturnValueHandler 總結(jié)
總體上 HandlerMethodReturnValueHandler 的設(shè)計體系和HandlerMethodArgumentResolver 差不多, 設(shè)計上夾雜著策略, 模版, 適配器 等等, 而我們常用的 @ResponseBody 就是通過RequestResponseBodyMethodProcessor 來解決的(PS: 根據(jù) Http中的 acceptMedia 與 HttpMessageConverter 來進行數(shù)據(jù)的轉(zhuǎn)換操作)!