前言
在SpringBoot/SpringMVC中,我们在@ResponseBody
注解标注的Controller
中直接返回对象,即可为请求返回所需类型的返回值(比如json格式)。本文就来谈谈返回值处理器的原理。
原文地址:https://xuedongyun.cn/post/44248/
返回值处理器原理
RequestMappingHandlerAdapter
有关参数解析的原理,可以查看我之前的文章:SpringBoot源码系列(5):参数解析
RequestMappingHandlerAdapter
执行了handleInternal
方法。代码中,主要设置了请求值解析器和返回值解析器,然后调用了invocableMethod.invokeAndHandle
进行处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @Nullable protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response); try { ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod); if (this.argumentResolvers != null) { invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers); } if (this.returnValueHandlers != null) { invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers); } ModelAndViewContainer mavContainer = new ModelAndViewContainer(); invocableMethod.invokeAndHandle(webRequest, mavContainer);
return getModelAndView(mavContainer, modelFactory, webRequest); } }
|
ServletInvocableHandlerMethod
在RequestMappingHandlerAdapter
中,调用了invocableMethod.invokeAndHandle
。我们现在查看ServletInvocableHandlerMethod
的invokeAndHandle
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs); setResponseStatus(webRequest); 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; }
try { this.returnValueHandlers.handleReturnValue( returnValue, getReturnValueType(returnValue), mavContainer, webRequest); } }
|
HandlerMethodReturnValueHandlerComposite
我们来看this.returnValueHandlers
的handleReturnValue
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @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("..."); } handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); }
|
其中,selectHandler
方法选择合适的返回值处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @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; }
|
this.returnValueHandlers
一共有15种
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| this.returnValueHandlers = {ArrayList} size = 15 0 = {ModelAndViewMethodReturnValueHandler} 1 = {ModelMethodProcessor} 2 = {ViewMethodReturnValueHandler} 3 = {ResponseBodyEmitterReturnValueHandler} 4 = {StreamingResponseBodyReturnValueHandler} 5 = {HttpEntityMethodProcessor} 6 = {HttpHeadersReturnValueHandler} 7 = {CallableMethodReturnValueHandler) 8 = {DeferredResultMethodReturnValueHandler} 9 = {AsyncTaskMethodReturnValueHandler} 10 = {ServletModelAttributeMethodProcessor} 11 = (RequestResponseBodyMethodProcessor} 12 = {ViewNameMethodReturnValueHandler} 13 = {MapMethodProcessor) 14 = {ServletModelAttributeMethodProcessor}
|
(例子一)ModelAndViewMethodReturnValueHandler
的supportsReturnType
方法:判断返回值类型是否继承自ModelAndView
(也即返回值能否转为ModelAndView
类型)
(例子二)RequestResponseBodyMethodProcessor
的supportsReturnType
方法:判断类或者方法,有没有加@ResponseBody
注解
HandlerMethodReturnValueHandler
以RequestResponseBodyMethodProcessor
为例,查看handler.handleReturnValue
方法
1 2 3 4 5 6 7 8 9 10 11 12
| @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);
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage); }
|
查看writeWithMessageConverters
方法。这里包含“内容协商”的核心原理:浏览器能接收的类型“并上”服务器能提供的类型,使用第一个作为使用的媒体类型。之后第二次遍历messageConverters
,寻找支持的Converter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
| protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
Object body; Class<?> valueType; Type targetType; if (value instanceof CharSequence) { body = value.toString(); valueType = String.class; targetType = String.class; } else { body = value; valueType = getReturnValueType(body, returnType); targetType = GenericTypeResolver.resolveType(getGenericType(returnType), returnType.getContainingClass()); }
if (isResourceType(value, returnType)) { } MediaType selectedMediaType = null; MediaType contentType = outputMessage.getHeaders().getContentType(); boolean isContentTypePreset = contentType != null && contentType.isConcrete(); if (isContentTypePreset) { ... selectedMediaType = contentType; } else { HttpServletRequest request = inputMessage.getServletRequest(); List<MediaType> acceptableTypes = getAcceptableMediaTypes(request); List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
List<MediaType> mediaTypesToUse = new ArrayList<>(); for (MediaType requestedType : acceptableTypes) { for (MediaType producibleType : producibleTypes) { if (requestedType.isCompatibleWith(producibleType)) { mediaTypesToUse.add(getMostSpecificMediaType(requestedType, producibleType)); } } } 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 (selectedMediaType != null) { selectedMediaType = selectedMediaType.removeQualityValue(); for (HttpMessageConverter<?> converter : this.messageConverters) { GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter<?>) converter : null); if (genericConverter != null ? ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) : converter.canWrite(valueType, selectedMediaType)) { body = getAdvice(). beforeBodyWrite(body, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) converter.getClass(), inputMessage, outputMessage); if (body != null) { Object theBody = body; LogFormatUtils.traceDebug("..."); addContentDispositionHeader(inputMessage, outputMessage); if (genericConverter != null) { genericConverter.write(body, targetType, selectedMediaType, outputMessage); } } return; } } } }
|
this.messageConverters
有9个,能否使用的判断由support
和canWrite
共同决定,下面只考虑了support
的部分
1 2 3 4 5 6 7 8 9 10 11
| this.messageConverters = {ArrayList} size = 9 0={ByteArrayHttpMessageConverter} 只支持Byte类型 1={StringHttpMessageConverter} 只支持String类型 2={StringHttpMessageConverter} 只支持String类型(这两个默认字符集不一样,UTF-8和ISO-8859-1) 3={ResourceHttpMessageConverter) 只支持返回值为Resource 4={ResourceRegionHttpMessageConverter} 只支持返回值为ResourceRegion 5={SourceHttpMessageConverter} 只支持返回值为DOMSource,SAXSource, StAXSource,StreamSource,Source 6={AllEncompassingFormHttpMessageConverter} 只支持返回值为MultiValueMap 7={MappingJackson2HttpMessageConverter} 直接返回true 8={MappingJackson2HttpMessageConverter} 直接返回true
|
MappingJackson2HttpMessageConverter
genericConverter.write
方法(此处为MappingJackson2HttpMessageConverter
),利用底层的jackson
包,ObjectMapper
转换的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Override public final void write(final T t, @Nullable final Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
final HttpHeaders headers = outputMessage.getHeaders(); addDefaultHeaders(headers, t, contentType);
... writeInternal(t, type, outputMessage); outputMessage.getBody().flush(); }
|
自定义MessageConverter
假设我们想自定义一个application/my-type
协议,请求将返回的User
以name-age的格式返回
我们首先要写一个类,实现HttpMessageConverter
,重写canWrite
,write
,getSupportedMediaTypes
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public class MyConverter implements HttpMessageConverter<User> {
@Override public boolean canRead(Class<?> clazz, MediaType mediaType) { return false; }
@Override public boolean canWrite(Class<?> clazz, MediaType mediaType) { return clazz.isAssignableFrom(User.class); }
@Override public List<MediaType> getSupportedMediaTypes() { return MediaType.parseMediaTypes("application/my-type"); }
@Override public User read(Class<? extends User> clazz, HttpInputMessage inputMessage) throws HttpMessageNotReadableException { return null; }
@Override public void write(User user, MediaType contentType, HttpOutputMessage outputMessage) throws HttpMessageNotWritableException, IOException { String data = user.getUserName() + "-" + user.getUserAge(); OutputStream body = outputMessage.getBody(); body.write(data.getBytes()); } }
|
利用WebMvcConfigurer
中的extendMessageConverters
方法,我们可以向容器中添加自定义的converter
1 2 3 4 5 6 7 8 9
| @Configuration public class WebConfig implements WebMvcConfigurer {
@Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { MyConverter myConverter = new MyConverter(); converters.add(myConverter); } }
|
最后,在我们前端的请求中,我们只需要在请求头指定:Accept=application,即可拿到我们想要的格式
总结和补充
- 标注了
@ResponseBody
注解 - 调用
RequestResponseBodyMethodProcessor
这个返回值处理器 - 调用各种
MessageConverter
进行数据的转换 MessageConverter
可以支持各种媒体类型数据的操作(读,写)- 内容协商找到合适的
MessageConverter
那原本已有的MessageConverter
是如何被添加的呢?我们来简单看一看源码:
在WebMvcAutoConfiguration
中添加了所有的默认MessageConverter
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Configuration(proxyBeanMethods = false) @Import(EnableWebMvcConfiguration.class) @EnableConfigurationProperties({ WebMvcProperties.class, WebProperties.class }) @Order(0) public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware {
@Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { this.messageConvertersProvider.ifAvailable( (customConverters) -> converters.addAll(customConverters.getConverters()) ); } }
|
继续查看,简单列出一下执行过的函数:
customConverters.getConverters()
->getDefaultConverters()
->defaultMessageConverters()
->getMessageConverters()
->addDefaultHttpMessageConverters()
这里自动为我们添加了很多converter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) { messageConverters.add(new ByteArrayHttpMessageConverter()); messageConverters.add(new StringHttpMessageConverter()); messageConverters.add(new ResourceHttpMessageConverter()); messageConverters.add(new ResourceRegionHttpMessageConverter()); if (!shouldIgnoreXml) { try { messageConverters.add(new SourceHttpMessageConverter<>()); } catch (Error err) { } } messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) { messageConverters.add(new AtomFeedHttpMessageConverter()); messageConverters.add(new RssChannelHttpMessageConverter()); }
if (!shouldIgnoreXml) { if (jackson2XmlPresent) { Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml(); if (this.applicationContext != null) { builder.applicationContext(this.applicationContext); } messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build())); } else if (jaxb2Present) { messageConverters.add(new Jaxb2RootElementHttpMessageConverter()); } }
if (kotlinSerializationJsonPresent) { messageConverters.add(new KotlinSerializationJsonHttpMessageConverter()); } if (jackson2Present) { Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json(); if (this.applicationContext != null) { builder.applicationContext(this.applicationContext); } messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build())); } else if (gsonPresent) { messageConverters.add(new GsonHttpMessageConverter()); } else if (jsonbPresent) { messageConverters.add(new JsonbHttpMessageConverter()); }
if (jackson2SmilePresent) { Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.smile(); if (this.applicationContext != null) { builder.applicationContext(this.applicationContext); } messageConverters.add(new MappingJackson2SmileHttpMessageConverter(builder.build())); } if (jackson2CborPresent) { Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.cbor(); if (this.applicationContext != null) { builder.applicationContext(this.applicationContext); } messageConverters.add(new MappingJackson2CborHttpMessageConverter(builder.build())); } }
|
比如其中jackson2Present
就是如此判断。这也就是为什么我们导入jackson-xml,就能使用的原因
1
| jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
|