- 适用场景
- 对应的HandlerMethodReturnValueHandler
- 消息转换器
- 内容协商
- 完成的执行流程
适用场景
基本介绍
AbstractMessageConverterMethodProcessor是处理返回值的一种策略。他下面有两个子类。其中一个是RequestResponseBoydMethodProcessor,负责处理标有@ResponseBody注解的方法的返回值。
返回值处理,说白了就是看我们提供的是什么类型的值,而浏览器能接受什么类型的值。然后通过内容协商进行转换。
内容协商说白了,就是能够判断出浏览器需要什么值,并且我们的提供的值(比如Person类型的person)转成浏览器能接收的值(比如xml)。
怎么判断浏览器能够接受那些类型?用内容协商管理去匹配合适的内容协商策略,然后通过内容协商策略去拿特定的值。
这里有很多种内容协商策略供选择。比如HeaderContentNegotiationStrategy、ParameterContentNegotiationStrategy,即头内容策略和参数内容策略。这些策略的作用就是拿到浏览器想要的类型(比如头内容策略是拿到request的header里的Accept字段,而参数内容策略是拿到request的format参数的值)
对应的HandlerMethodReturnValueHandler
AbstractMessageConvertMethodProcessor
继承关系
实现了返回值处理接口。继承了参数解析类。所以同时具有 参数解析和返回值处理两种功能。
他的子类有 RequestResponseBodyMethodProcessor,这个子类可以处理标有 @ResonseBody 的方法的返回值。但是处理的过程还是主要由当前类来完成。
属性:只介绍两个,如下contentNegotiationManager、messageConverters(这个在他的父类里)
顾名思义,就是内容协商管理。ContentNegotiationManager类的主要属性和方法可以去下面看关于此类的介绍。
总之,这个属性的作用就是可以从所有的的内容协商策略里找到合适的策略并返回。 这些内容策略有很多,比如头内容策略(拿到request的header里的Accept字段)、参数内容策略(拿到request的format参数的值),具体的内容协商策略有具体的解析返回类型的方式(resolveMediaTypes)
messageConverters:就是消息转换,具体的也可以去下面看。总之,就是有很多种消息转换器内含于这里
行为:如下,主要是writeWithMessageConverters()。
作用是得到浏览器能接收的类型,服务器能产生的类型,找到之后进行转化。 这个方法调用了本类里各种方法
writeWithMessageConverters()详解
下面分开讲讲:
MediaType contentType = outputMessage.getHeaders().getContentType()
- 判断当前响应头中是否已经有确定的媒体类型。MediaType
- 比如如果拦截器拦截的时候就已经往request写死了他的接收类型,那么这时候就可以直接获取出来·
List acceptableTypes = getAcceptableMediaTypes(request);
获取客户端(PostMan、浏览器)支持接收的内容类型acceptableTypes。怎么获取呢?
调用自身getAcceptableMediaTypes(request),目的是得到客户端支持接收的内容类型
- 调用了 内容协商管理器类 的resolveMediaTypes(),目的是匹配合适的内容协商类(这里是HeaderContentNegotiationStrategy),目的是让他真正的去获取
- contentNegotiationManager 内容协商管理器 默认使用基于请求头的策略
- 调用HeaderContentNegotiationStrategy#resolveMediaTypes,目的是**去获取客户端支持接收的内容类型**
- 通过调用原生的,request.getHeaderValues(HttpHeaders.ACCEPT); 目的是获取浏览器里的Accept字段值(这里得到【application/xml】)
List producibleTypes = getProducibleMediaTypes(request, valueType, targetType);
遍历循环所有当前系统的 MessageConverter,看谁支持操作这个对象(Person),找到下面四个
找到支持操作Person的converter,把converter支持的媒体类型统计出来。找到下面10种服务端能力【10种、json、xml】
mediaTypesToUser
前面不是找到了10种能力么,而且acceptableTypes只有xml一种,那么就拿这10种去匹配这一种,
进行内容协商的最佳匹配媒体类型,找到了能支持xml的两种
用 支持 将对象转为 最佳匹配媒体类型 的converter。调用它进行转化 。
再次遍历所有的messageConvert,找到能把这种类型(xml)写出去的convert,然后让他去写。就算完成了
导入了jackson处理xml的包,xml的converter就会自动进来
WebMvcConfigurationSupport
jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
if (jackson2XmlPresent) {
Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();
if (this.applicationContext != null) {
builder.applicationContext(this.applicationContext);
}
messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
}
RequestResponseBodyMethodProcessor
处理有ResponseBody注解的方法的返回值
行为:supportsReturnType()、handleReturnValue()
supportsReturnType()
handleReturnValue()
消息转换器
是什么
消息转换器相关类
HttpMessageConvert:判断是否支持将某class对象转成MediaType类型的数据
讲一下HttpMessageConverter,是个接口,有非常多实现类。目的是为了将 Class类型转成json、xml等各种类型。这些实现类负责各种不同的类的转换(比如String-> json、byte-> json、Class -> xml)
首先自己的messageConverters存放着所有的信息转换类(即HttpMessageConvert的各种实现类),遍历找出所有能够处理当前Class转换的类。
例:Person对象转成json
canRead:能不能把json转成person
canWrite:能不能把person转成json
继承关系
行为:canRead()、canWrite()、read()、write()、getSupportedMediaTypes()
canWrite():判断当前HMC支不支持,比如把person对象以json方式写出去
内容协商
是什么
简单来说,就是根据不同的要求,能够返回不同的内容。比如如果用浏览器,就返回json格式,如果用postman的话,就返回xml格式的数据。 这就是内容协商
内容协商相关类
简略介绍
主要类就两个:内容协商策略接口,内容协商策略管理。
内容协商策略接口,里面就一个方法resolverMediaTypes(),作用就是拿到浏览器能够接收的MediaType类型。
而内容协商管理,里面也有resolverMediaTypes(),作用就是匹配各种策略类,返回合适的策略类。
比如,如果浏览器请求是 http://localhost:8080/person?format=xml,就用参数策略类
如果浏览器直接发请求,就用头内容策略类。
不管用啥策略,最后都是调用策略里的resolveMediaTypes(),返回浏览器能接收的MediaType值
内容协商策略:ContentNegotiationStrategy,接口。有很多实现类,比如参数内容策略,头内容策略
继承关系
属性:只有一个MEDIA_TYPE_ALL_LIST,即所有的媒体类型的集合
行为:resolverMediaTypes,解析MediaType类型的数据
这里各种策略,内部里只有一个方法 resolveMediaTypes(),作用是拿到浏览器所能接收的所有MediaType类型,并且放到list,返回该list。这个list里放的就是通过这种解析方式解析出来的 浏览器所能接收的所有mediaType类型。
文件类型解析:MediaTypeFileExtensionResolver,接口。暂时用不到
继承关系
行为:resolveFileExtensions()、getAllFileExtensions()
内容协商管理:ContentNegotiationManager,类。作用是匹配各种策略类以找到合适的策略类。
属性:strategies(内容协商策略集合)、resolvers(文件解析集合)
行为:getStrategies()、getStrategy()、resolverMediaType()、resolveFileExtensions()。即也得实现接口的方法。
即get所有策略、get单个策略等。重点说一下resolveMediaTypes()
媒体类型:MediaType,类
里面有各种属性,都是规定媒体类型。 比如 application/xml,application/json,application/cbor,text/html等
一个MediaType类,就对应一个上面的字符串。比如下面的。
完成的执行流程
InvocableHandlerMethod负责参数解析,解析完之后doInvoke()得到返回值。
ServletInvocableHandlerMethod负责返回值解析。
处理标有@ResponseBody的方法的返回值的时候,通过遍历所有的returnValueHandlers找到RequestResponseBodyMethodProcessor,并且调用handleReturnValue()去处理返回值,这个方法里又调用了父类的writeWithMessageConverters()去处理。
这里,先拿到浏览器能接收的类型,怎么拿上面说的很清楚了,不重复了。
然后,拿到服务器能产生的类型,怎么拿?
讲一下HttpMessageConverter,是个接口,有非常多实现类。目的是为了将 Class类型转成json、xml等各种类型。这些实现类负责各种不同的类的转换(比如String-> json、byte-> json、Class -> xml)
首先自己的messageConverters存放着所有的信息转换类(即HttpMessageConvert的各种实现类),遍历找出所有能够处理当前Class转换的类。找到之后,得到这个convert支持处理的所有MediaType,放到List
然后继续遍历,继续找到合适的convert,得到他支持处理的所有MediaType,放到List
得到的这个list就是服务器能产生的所有的MediaType,然后进行最佳匹配。匹配完之后再次遍历所有的convert,找到合适的convert,调用其write()把Person改成对应的MediaType类型(比如xml类型)
处理流程:
这里处理返回值的流程比较复杂,不再详细的去展开,而是蜻蜓点水,有个大致的轮廓。
如上,handleReturnValue()调用下面的 writeWithMessageConverters()
AbstractMessageConverterMethodProcessor#writeWithMessageConverters()
然后遍历拿到所有的能够可用的媒体类型,并加入到集合,接下来就想办法去处理这些类型,怎么处理?
遍历所有的HttpMessageConvert,看谁能处理,判断过程如下
判断此convert是不是GenericHttpMessageConvert的实例,如果是,强转,否则为null
然后看convert能不能写,进去,里面调用了support方法。判断是否支持byte、String等。
对10个进行遍历,发现是,0只支持byte型,1只支持String型…….. 最后发现来到7,发现7可以。为什么可以?看他的supports方法,返现直接返回为true,即不管什么类型,我都支持
继续流程,找到合适的HttpMessageConvert之后,调用这个convert的write()去写
后面省略了,反正就是想办法写进去,对怎么写进去感兴趣的话,自己debug看看
所以最终是MappingJackson2HttpMessageConvert 把对象转成json,怎么转的(利用底层的jackson的objectMapper转换的)
总结
- 1、返回值处理器判断是否支持这种类型返回值 supportsReturnType
- 2、返回值处理器调用 handleReturnValue 进行处理
- 3、RequestResponseBodyMethodProcessor 可以处理返回值标了@ResponseBody 注解的。
- 利用 MessageConverters 进行处理 将数据写为json
- 1、内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型)
- 2、服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据,
- 3、SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能处理?
- 1、得到MappingJackson2HttpMessageConverter可以将对象写为json
- 2、利用MappingJackson2HttpMessageConverter将对象转为json再写出去。
- 利用 MessageConverters 进行处理 将数据写为json