RequestMappingHandlerAdapter是Spring Web MVC中針對@Controller和@RequestMapping體系的處理器適配器,本文對RequestMappingHandlerAdapter的組成、初始化以及同步請求處理流程進行詳細梳理和總結。 ...
RequestMappingHandlerAdapter
是日常項目中使用最多的HandlerAdapter
實現類。
它還有一個抽象父類AbstractHandlerMethodAdapter
,顧名思義,是專門用來處理HandlerMethod
類型的handler
。具體可以看AbstractHandlerMethodAdapter#supports
方法:
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
通過之前的學習可以知道,RequestMappingHandlerMapping
獲取的handler
就是HandlerMethod
類型的。
RequestMappingHandlerMapping
和RequestMappingHandlerAdapter
就像一對孿生兄弟:
RequestMappingHandlerMapping
負責根據request
找到映射的handler
RequestMappingHandlerAdapter
負責根據handler
執行對應的方法
我們先總結RequestMappingHandlerAdapter
處理handler
的核心流程:
- 將
request
和response
封裝成ServletWebRequest
對象。 - 將
handler
封裝成ServletInvocableHandlerMethod
對象invocableMethod
。 - 為
invocableMethod
設置argumentResolvers
、returnValueHandlers
、dataBinderFactory
和parameterNameDiscoverer
等工具。 - 解析請求參數。
- 執行方法。
- 處理返回值。
實際上,RequestMappingHandlerAdapter
處理handler
過程中還有許多細節,比如前後端不分離項目的視圖相關處理(沒有必要花費時間深入學習),非同步請求的相關處理(會另外寫文章)。
0 預備知識
RequestMappingHandlerAdapter
中有許多成員變數,在請求處理過程中起著重要的作用。
0.1 argumentResolvers
argumentResolvers
是參數解析器,RequestMappingHandlerAdapter
使用argumentResolvers
進行參數解析。
簡單來說,就是將HTTP請求中的數據,轉換成handler
方法中的形參對象。
argumentResolvers
使用了組合模式,它的類型是HandlerMethodArgumentResolverComposite
,其內部緩存HandlerMethodArgumentResolver
對象,用來進行參數解析。
HandlerMethodArgumentResolverComposite
中包含argumentResolvers
和argumentResolverCache
兩個成員變數。在初始化時,會將所有配置的參數解析器緩存到argumentResolvers
中。第一次解析參數時,會遍歷argumentResolvers
獲取對應參數解析器,並緩存到argumentResolverCache
中,後續再次解析該參數可直接從鍵值對中獲取,提高效率。
實際進行參數解析的是HandlerMethodArgumentResolver
實現類。它們使用了策略模式,通過supportsParameter()
方法獲取支持的參數解析器,通過resolveArgument()
方法進行參數解析。
0.2 customArgumentResolvers
customArgumentResolvers
是用於緩存開發人員自定義的參數解析器,即通過WebMvcConfigurer#addArgumentResolvers()
方法添加的解析器。
在RequestMappingHandlerAdapter
初始化時,會將customArgumentResolvers
中的自定義參數解析器添加到argumentResolvers
中。
0.3 returnValueHandlers
returnValueHandlers
是返回值處理器,它可以對控制層業務返回值進行處理。
例如,對@ResponseBody
標註的返回值進行JSON格式化,並寫到輸出流。
returnValueHandlers
使用了組合模式,它的類型是HandlerMethodReturnValueHandlerComposite
,其內部緩存HandlerMethodReturnValueHandler
對象,用來進行返回值處理。
0.4 customReturnValueHandlers
customReturnValueHandlers
是用於緩存開發人員自定義的參數解析器,即通過WebMvcConfigurer#addReturnValueHandlers()
方法添加的解析器。
在RequestMappingHandlerAdapter
初始化時,會將customReturnValueHandlers
中的自定義參數解析器添加到returnValueHandlers
中。
1 初始化流程
在RequestMappingHandlerAdapter
內部,有兩個方法用於初始化。一個是構造函數,另一個是實現org.springframework.beans.factory.InitializingBean
的afterPropertiesSet()
方法。
在Spring Boot中,會在WebMvcConfigurationSupport
中進行完整的初始化。
1.1 構造函數
構造函數中主要是對messageConverters
進行初始化,添加一些必備的消息轉換器。實際上,WebMvcConfigurationSupport
中會進行覆蓋,因此不過多描述:
public RequestMappingHandlerAdapter() {
this.messageConverters = new ArrayList<>(4);
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
if (!shouldIgnoreXml) {
try {
this.messageConverters.add(new SourceHttpMessageConverter<>());
}
catch (Error err) {
// Ignore when no TransformerFactory implementation is available
}
}
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
1.2 afterPropertiesSet()
在RequestMappingHandlerAdapter#afterPropertiesSet()
方法中,會對argumentResolvers
、initBinderArgumentResolvers
和returnValueHandlers
等進行初始化:
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
initControllerAdviceCache();
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
1.2.1 initControllerAdviceCache
在RequestMappingHandlerAdapter#initControllerAdviceCache()
方法中,會從容器中獲取所有@ControllerAdvice
標註的bean
。然後緩存這些bean
中標註@RequestMapping&@ModelAttribute
(modelAttributeAdviceCache
)和@InitBinder
(initBinderAdviceChache
)等註解的方法,並且直接緩存實現RequestBodyAdvice
或ResponseBodyAdvice
的bean
(requestResponseBodyAdvice
)。
private void initControllerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
if (!attrMethods.isEmpty()) {
this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
}
Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
if (!binderMethods.isEmpty()) {
this.initBinderAdviceCache.put(adviceBean, binderMethods);
}
if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add(adviceBean);
}
}
if (!requestResponseBodyAdviceBeans.isEmpty()) {
this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
}
}
1.2.2 getDefaultXxx()方法
通過getDefaultArgumentResolvers()
、getDefaultInitBinderArgumentResolvers()
和getDefaultResurnValueHandlers()
方法分別對argumentResolvers
、initBinderArgumentResolvers
和returnValueHandlers
進行初始化。
在這些getDefaultXxx()
方法中,一方面會按一定順序添加一系列預設的處理器對象,另一方面會通過getCustomXxx()
方法獲取開發人員自定義的處理器對象(可通過WevMvcConfigurer
添加)。
例如,RequestMappingHandlerAdapter#getDefaultArgumentResolvers()
方法會添加一系列預設的參數解析器,並且通過getCustomArgumentResolvers()
方法獲取開發人員自定義的參數解析器:
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(30);
// Annotation-based argument resolution
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new PathVariableMapMethodArgumentResolver());
resolvers.add(new MatrixVariableMethodArgumentResolver());
resolvers.add(new MatrixVariableMapMethodArgumentResolver());
resolvers.add(new ServletModelAttributeMethodProcessor(false));
resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
resolvers.add(new RequestHeaderMapMethodArgumentResolver());
resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
resolvers.add(new SessionAttributeMethodArgumentResolver());
resolvers.add(new RequestAttributeMethodArgumentResolver());
// Type-based argument resolution
resolvers.add(new ServletRequestMethodArgumentResolver());
resolvers.add(new ServletResponseMethodArgumentResolver());
resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));
resolvers.add(new RedirectAttributesMethodArgumentResolver());
resolvers.add(new ModelMethodProcessor());
resolvers.add(new MapMethodProcessor());
resolvers.add(new ErrorsMethodArgumentResolver());
resolvers.add(new SessionStatusMethodArgumentResolver());
resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
if (KotlinDetector.isKotlinPresent()) {
resolvers.add(new ContinuationHandlerMethodArgumentResolver());
}
// Custom arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
resolvers.add(new PrincipalMethodArgumentResolver());
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
1.3 WebMvcConfigurationSupport
在WebMvcConfigurationSupport#requestMappingHandlerAdapter()
中,會完成requestMappingHandlerAdapter
的bean
的創建,對contentNegotiationManager
、messageConverters
、webBindingInitializer
、customArgumentResolvers
和customReturnValueHandlers
等基礎成員變數,以及非同步請求的taskExecutor
、asyncRequestTimeout
、callableInterceptors
和deferredResultInterceptors
等成員變數進行初始化:
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(
@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
@Qualifier("mvcValidator") Validator validator) {
RequestMappingHandlerAdapter adapter = createRequestMappingHandlerAdapter();
adapter.setContentNegotiationManager(contentNegotiationManager);
adapter.setMessageConverters(getMessageConverters());
adapter.setWebBindingInitializer(getConfigurableWebBindingInitializer(conversionService, validator));
adapter.setCustomArgumentResolvers(getArgumentResolvers());
adapter.setCustomReturnValueHandlers(getReturnValueHandlers());
if (jackson2Present) {
adapter.setRequestBodyAdvice(Collections.singletonList(new JsonViewRequestBodyAdvice()));
adapter.setResponseBodyAdvice(Collections.singletonList(new JsonViewResponseBodyAdvice()));
}
AsyncSupportConfigurer configurer = getAsyncSupportConfigurer();
if (configurer.getTaskExecutor() != null) {
adapter.setTaskExecutor(configurer.getTaskExecutor());
}
if (configurer.getTimeout() != null) {
adapter.setAsyncRequestTimeout(configurer.getTimeout());
}
adapter.setCallableInterceptors(configurer.getCallableInterceptors());
adapter.setDeferredResultInterceptors(configurer.getDeferredResultInterceptors());
return adapter;
}
在初始化過程中,一方面會為這些成員添加一系列預設對象,另一方面會從WebMvcConfigurer
中獲取開發人員自定義的對象。
2 同步請求處理流程
首先,DispatcherServlet
會調用HandlerAdapter
介面的handle()
方法。
AbstractHandlerMethodAdapter
對handle()
方法的實現只是做了一個類型轉換:
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return handleInternal(request, response, (HandlerMethod) handler);
}
AbstractHandlerMethodAdapter#handleInternal()
是一個抽象方法,會由子類具體去實現。
RequestMappingHandlerAdapter#handlerInternal()
方法中會進行一些請求判斷和緩存處理(省略),它的核心是在invokeHandlerMethod()
方法:
protected ModelAndView handleInternal(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ModelAndView mav;
mav = invokeHandlerMethod(request, response, handlerMethod);
return mav;
}
2.1 預處理:添加處理器
在RequestMappingHandlerAdapter#invokeHandlerMethod()
方法中,會進行如下處理:
- 將
request
和response
封裝成ServletWebRequest
對象,便於後續處理。 - 將
handler
封裝成ServletInvocableHandlerMethod
對象invocableMethod
。 - 為
invocableMethod
設置argumentResolvers
(參數解析)、returnValueHandlers
(返回值處理)、dataBinderFactory
(數據綁定和校驗)和parameterNameDiscoverer
(形參名字解析)等組件,用作後續方法處理的工具。這些組件都來自RequestMappingHandlerAdapter
的成員變數。 - 最後會調用
invocableMethod
的invokeAndHandle()
方法進行實際處理。
RequestMappingHandlerAdapter#invokeHandlerMethod()
具體源碼如下:
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
// 1、將`request`和`response`封裝成`ServletWebRequest`對象
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 2、將`handler`封裝成`ServletInvocableHandlerMethod`對象`invocableMethod`
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
// 3、為`invocableMethod`設置`argumentResolvers`、`returnValueHandlers`、`dataBinderFactory`和`parameterNameDiscoverer`等工具
if (this.argumentResolvers != null) {
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// 4、處理請求
invocableMethod.invokeAndHandle(webRequest, mavContainer);
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
ServletInvocableHandlerMethod#invokeAndHandle()
方法會調用請求,並且對返回值進行處理:
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 1、調用請求
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
// 省略相關代碼
// 2、返回值處理
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
2.2 形參對象解析
在InvocableHandlerMethod#invokeForRequest()
方法中,會進行參數解析(將request
中的數據解析成handler
方法的形參對象),然後通過反射調用對應方法,獲取返回值:
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 1、參數解析
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
// 2、調用方法
return doInvoke(args);
}
在InvocableHandlerMethod#getMethodArgumentValues()
方法中,會通過反射獲取handler
方法的形參,然後使用resolvers
對一個個形參進行解析。
根據形參的類型不同(HttpServletRequest等),形參上標註的註解不同(@RequestBody
等),會調用不同的解析器實現類進行處理。
根據解析器實現類的不同,在解析過程中,會進行數據綁定、消息轉換和參數校驗:
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
// 1、獲取方法的形參信息
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
// 遍歷方法形參
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
// 2、形參解析
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// Leave stack trace for later, exception may actually be resolved and handled...
if (logger.isDebugEnabled()) {
String exMsg = ex.getMessage();
if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) {
logger.debug(formatArgumentError(parameter, exMsg));
}
}
throw ex;
}
}
return args;
}
2.3 執行方法
回到InvocableHandlerMethod#invokeForRequest()
方法,解析方法形參後,會調用InvocableHandlerMethod#doInvoke()
方法,通過反射調用方法,並傳入handler
對應的控制層bean
作為觸發對象,以及上述形參對象:
protected Object doInvoke(Object... args) throws Exception {
Method method = getBridgedMethod();
try {
return method.invoke(getBean(), args);
}
catch (IllegalArgumentException ex) {
// 省略相關代碼
}
}
2.4 返回值處理
回到ServletInvocableHandlerMethod#invokeAndHandle()
方法,此時獲取了handler
方法執行完成的返回值,會調用HandlerMethodReturnValueHandlerComposite#handleReturnValue()
方法對返回值進行處理。首先會根據返回值信息MethodParameter
對象查找支持的返回值處理器HandlerMethodReturnValueHandler
,然後使用該處理器對返回值進行處理:
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 1、查找返回值處理器
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
// 2、返回值處理
handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}
在HandlerMethodReturnValueHandlerComposite#selectHandler
方法中,會遍歷returnValueHandlers
,調用其HandlerMethodReturnValueHandler#supportsReturnType
實現方法找到對應返回值處理器。:
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;
}
找到返回值處理器後,就可以通過其handleReturnValue()
方法對返回值進行處理。
舉個有實戰意義的例子,@ResponseBody
的HandlerMethodReturnValueHandler
實現類是RequestResponseBodyMethodProcessor
。
RequestResponseBodyMethodProcessor
的supportsReturnType()
方法會判斷返回值是否標有ResponseBody
註解:
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
RequestResponseBodyMethodProcessor
的handleReturnValue()
方法會根據返回的Content-Type
對返回值進行對應格式化,並寫入到輸出流中:
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);
}
至此,我們走完了RequestMappingHandlerAdapter
對同步請求的完整處理流程(前後端分離)。簡單來說,會經過一下主要步驟:
- 初始化請求處理的工具:
argumentResolvers
、returnValueHandlers
、binderFactory
和parameterNameDiscoverer
等。 - 解析形參對象
- 執行方法
- 返回值處理
實際上RequestMappingHandlerAdapter
中還會對非同步請求進行處理,這部分我們會在之後的文章進行詳細介紹。
3 HandlerMethodArgumentResolver實現類
3.1 RequestResponseBodyMethodProcessor
RequestResponseBodyMethodProcessor
是前後端分離項目中使用最多的HandlerMethodArgumentResolver
實現類,它可以處理@RequestBody
標註的形參。
3.1.1 supportsParameter()方法
RequestResponseBodyMethodProcessor#supportsParameter()
方法會判斷形參上是否標註@RequestBody
註解:
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
3.1.2 resolveArgument()方法
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);
}
AbstractMessageConverterMethodArgumentResolver#readWithMessageConverters()
方法會根據Content-Type
從輸入流讀取數據,並創建成形參對象:
protected <T> Object readWithMessageConverters(HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType) throws IOException, HttpMediaTypeNotSupportedException, HttpMessageNotReadableException {
// 獲取請求Content-Type
MediaType contentType;
boolean noContentType = false;
try {
contentType = inputMessage.getHeaders().getContentType();
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotSupportedException(ex.getMessage());
}
if (contentType == null) {
noContentType = true;
contentType = MediaType.APPLICATION_OCTET_STREAM;
}
// 獲取形參類型
Class<?> contextClass = parameter.getContainingClass();
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;
// 根據Content-Type使用對應messageConverter讀取並轉換數據
EmptyBodyCheckingHttpInputMessage message = null;
try {
message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
for (HttpMessageConverter<?> converter : this.messageConverters) {
Class<HttpMessageConverter<?>> converterType = (Class<HttpMessageConverter<?>>) converter.getClass();
GenericHttpMessageConverter<?> genericConverter =
(converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null);
// 根據Content-Type獲取對應的messageConverter
if (genericConverter != null ? genericConverter.canRead(targetType, contextClass, contentType) :
(targetClass != null && converter.canRead(targetClass, contentType))) {
if (message.hasBody()) {
// RequestBodyAdvice#beforeBodyRead()處理
HttpInputMessage msgToUse =
getAdvice().beforeBodyRead(message, parameter, targetType, converterType);
// 讀取並轉換數據
body = (genericConverter != null ? genericConverter.read(targetType, contextClass, msgToUse) :
((HttpMessageConverter<T>) converter).read(targetClass, msgToUse));
// RequestBodyAdvice#afterBodyRead()處理
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;
}
AbstractMessageConverterMethodArgumentResolver#validateIfApplicable()
方法會對標註javax.validation.Valid
、org.springframework.validation.annotation.Validated
以及以Valid
開頭的自定義註解進行參數校驗:
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
Annotation[] annotations = parameter.getParameterAnnotations();
for (Annotation ann : annotations) {
Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
if (validationHints != null) {
binder.validate(validationHints);
break;
}
}
}
4 HandlerMethodReturnValueHandler實現類
4.1 RequestResponseBodyMethodProcessor
RequestResponseBodyMethodProcessor
是前後端分離項目中使用最多的HandlerMethodReturnValueHandler
實現類,它可以處理@ResponseBody
標註的返回值。
4.1.1 supportsReturnType()方法
RequestResponseBodyMethodProcessor#supportsReturnType()
方法會判斷類或方法上是否標註@RequestBody
註解:
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
4.1.2 handleReturnValue()方法
RequestResponseBodyMethodProcessor#handleReturnValue()
方法會根據響應的Content-Type
,將返回值格式化成對應數據格式,寫道輸出流進行響應:
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);
}
實際業務在AbstractMessageConverterMethodProcessor#writeWithMessageConverters()
方法,
protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
Object body;
Class<?> valueType;
Type targetType;
// 如果返回值是CharSequence類型,valueType和targetType都設置成String類型
if (value instanceof CharSequence) {
body = value.toString();
valueType = String.class;
targetType = String.class;
}
// 如果返回值不是CharSequence,valueType設置成對應返回值類型,targetType會設置成解析泛型後的返回值類型
else {
body = value;
valueType = getReturnValueType(body, returnType);
targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass());
}
// 如果是返回值繼承自Resource
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());
}
}
}
// 獲取響應的Content-Type
MediaType selectedMediaType = null;
MediaType contentType = outputMessage.getHeaders().getContentType();
boolean isContentTypePreset = contentType != null && contentType.isConcrete();
if (isContentTypePreset) {
if (logger.isDebugEnabled()) {
logger.debug("Found 'Content-Type:" + contentType + "' in response");
}
selectedMediaType = contentType;
}
else {
HttpServletRequest request = inputMessage.getServletRequest();
List<MediaType> acceptableTypes;
try {
acceptableTypes = getAcceptableMediaTypes(request);
}
catch (HttpMediaTypeNotAcceptableException ex) {
int series = outputMessage.getServletResponse().getStatus() / 100;
if (body == null || series == 4 || series == 5) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring error response content (if any). " + ex);
}
return;
}
throw ex;
}
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));
}
}
}
if (mediaTypesToUse.isEmpty()) {
if (logger.isDebugEnabled()) {
logger.debug("No match for " + acceptableTypes + ", supported: " + producibleTypes);
}
if (body != null) {
throw new HttpMediaTypeNotAcceptableException(producibleTypes);
}
return;
}
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);
}
}
// 根據響應Content-Type格式化返回值,並寫到輸出流
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
// 根據響應Content-Type獲取對應的messageConverter
if (genericConverter != null ?
((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {
// ResponseBodyAdvice的beforeBodyWrite()處理
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) + "]");
addContentDispositionHeader(inputMessage, outputMessage);
// 通過messageConverter格式化返回值,並寫到輸出流
if (genericConverter != null) {
genericConverter.write(body, targetType, selectedMediaType, outputMessage);
}
else {
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Nothing to write: null body");
}
}
return;
}
}
}
}