数据响应与内容协商
1、响应JSON
1.1、jackson.jar+@ResponseBody
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
web场景自动引入了json场景
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.3.4.RELEASE</version>
<scope>compile</scope>
</dependency>
给前端自动返回json数据;
【默认返回值处理器】
private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);
// Single-purpose return value types
handlers.add(new ModelAndViewMethodReturnValueHandler());
handlers.add(new ModelMethodProcessor());
handlers.add(new ViewMethodReturnValueHandler());
handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters(),
this.reactiveAdapterRegistry, this.taskExecutor, this.contentNegotiationManager));
handlers.add(new StreamingResponseBodyReturnValueHandler());
handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
handlers.add(new HttpHeadersReturnValueHandler());
handlers.add(new CallableMethodReturnValueHandler());
handlers.add(new DeferredResultMethodReturnValueHandler());
handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));
// Annotation-based return value types
handlers.add(new ModelAttributeMethodProcessor(false));
handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
this.contentNegotiationManager, this.requestResponseBodyAdvice));
// Multi-purpose return value types
handlers.add(new ViewNameMethodReturnValueHandler());
handlers.add(new MapMethodProcessor());
// Custom return value types
if (getCustomReturnValueHandlers() != null) {
handlers.addAll(getCustomReturnValueHandlers());
}
// Catch-all
if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
}
else {
handlers.add(new ModelAttributeMethodProcessor(true));
}
return handlers;
}
1.2 默认返回值处理器
顶层接口HandlerMethodReturnValueHandler
注意这个方法:supportsReturnType
1、ModelAndViewMethod
ModelAndViewMethodReturnValueHandler
//处理返回值类型是ModelAndView的
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return ModelAndView.class.isAssignableFrom(returnType.getParameterType());
}
2、ModelMethod
ModelMethodProcessor
//处理返回值类型是Model
@Override
public boolean supportsParameter(MethodParameter parameter) {
return Model.class.isAssignableFrom(parameter.getParameterType());
}
3、ViewMethod
ViewMethodReturnValueHandler
//处理返回值类型是View
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return View.class.isAssignableFrom(returnType.getParameterType());
}
4、ResponseBodyEmitter
ResponseBodyEmitterReturnValueHandler
@Override
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> bodyType = ResponseEntity.class.isAssignableFrom(returnType.getParameterType()) ?
ResolvableType.forMethodParameter(returnType).getGeneric().resolve() :
returnType.getParameterType();
return (bodyType != null && (ResponseBodyEmitter.class.isAssignableFrom(bodyType) ||
this.reactiveHandler.isReactiveType(bodyType)));
}
5、StreamingResponseBody
StreamingResponseBodyReturnValueHandler
@Override
public boolean supportsReturnType(MethodParameter returnType) {
if (StreamingResponseBody.class.isAssignableFrom(returnType.getParameterType())) {
return true;
}
else if (ResponseEntity.class.isAssignableFrom(returnType.getParameterType())) {
Class<?> bodyType = ResolvableType.forMethodParameter(returnType).getGeneric().resolve();
return (bodyType != null && StreamingResponseBody.class.isAssignableFrom(bodyType));
}
return false;
}
6、HttpEntityMethod
HttpEntityMethodProcessor
//处理返回值类型是HttpEntity或者RequestEntity
@Override
public boolean supportsParameter(MethodParameter parameter) {
return (HttpEntity.class == parameter.getParameterType() ||
RequestEntity.class == parameter.getParameterType());
}
7、HttpHeadersReturn
HttpHeadersReturnValueHandler
//处理返回值类型是HttpHeaders
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return HttpHeaders.class.isAssignableFrom(returnType.getParameterType());
}
8、CallableMethod
CallableMethodReturnValueHandler
//处理返回值类型是Callable
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return Callable.class.isAssignableFrom(returnType.getParameterType());
}
9、DeferredResultMethod
DeferredResultMethodReturnValueHandler
@Override
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> type = returnType.getParameterType();
return (DeferredResult.class.isAssignableFrom(type) ||
ListenableFuture.class.isAssignableFrom(type) ||
CompletionStage.class.isAssignableFrom(type));
}
10、AsyncTaskMethod 异步任务处理
AsyncTaskMethodReturnValueHandler
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return WebAsyncTask.class.isAssignableFrom(returnType.getParameterType());
}
11、ModelAttributeMethod
ModelAttributeMethodProcessor
@Override
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
(this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
}
12、【处理注解@RequestBody】===核心常用
RequestResponseBodyMethodProcessor
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
13、
ViewNameMethodReturnValueHandler
@Override
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> paramType = returnType.getParameterType();
return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
}
14、返回值类型是Map的
MapMethodProcessor
@Override
public boolean supportsParameter(MethodParameter parameter) {
return (Map.class.isAssignableFrom(parameter.getParameterType()) &&
parameter.getParameterAnnotations().length == 0);
}
15、
ModelAndViewResolverMethodReturnValueHandler
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return true;
}
16、
ModelAttributeMethodProcessor
@Override
public boolean supportsParameter(MethodParameter parameter) {
return (parameter.hasParameterAnnotation(ModelAttribute.class) ||
(this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
}
1.3执行链路
请求链路
DispatcherServlet
ha.handle(processedRequest, response, mappedHandler.getHandler())
【下面是拿到了一个请求的适配处理器:基于分析:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter】
1、RequestMappingHandlerAdapter里面有些什么?
1)、HandlerMethodReturnValueHandlerComposite returnValueHandlers//属性,方法方回执处理器(组合)--这个里面包含很多返回值处理器
HandlerMethodReturnValueHandlerComposite:属性--List<HandlerMethodReturnValueHandler> returnValueHandlers
2)、returnValueHandlers在什么时候初始化的呢?
1)、HandlerMethodReturnValueHandlerComposite-public void afterPropertiesSet()方法【学习了spring底层架构,明白这个是一个属性设置器,在bean初始化完成以后调用】
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();//通过获取默认的返回值处理器,然后构建一个HandlerMethodReturnValueHandlerComposite
//然后把默认的返回值处理器通过添加进去【this:RequestMappingHandlerAdapter】
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
2)、默认返回值处理器怎么来?getDefaultReturnValueHandlers()
【1.2 默认返回值处理器】有分析--初始化完成后就加入到--HandlerMethodReturnValueHandlerComposite中
3)、RequestMappingHandlerAdapter.returnValueHandlers=new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);就完成了初始化
--》handleInternal(request, response, (HandlerMethod) handler)
【分析返回值处理】
returnValueHandlers就是HandlerMethodReturnValueHandlerComposite
--》this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest)
拿到返回时处理器进行处理returnValueHandlers
1)、HandlerMethodReturnValueHandlerComposite.handleReturnValue
//Object returnValue(返回值类型object), MethodParameter returnType返回值方法参数类型
HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);查找可以处理的返回值处理器
@Nullable
private HandlerMethodReturnValueHandler selectHandler(@Nullable Object value, MethodParameter returnType) {
boolean isAsyncValue = isAsyncReturnValue(value, returnType);//看看是不是异步的:
//【看看支不支持异步】handler instanceof AsyncHandlerMethodReturnValueHandler && ((AsyncHandlerMethodReturnValueHandler) handler).isAsyncReturnValue(value, returnType)
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
continue;//因为上面isAsyncReturnValue判断过异步了,这里如果是,继续循环遍历
}
//在【1.2 默认返回值处理器】里面说明了,具体返回值行了的处理器支持
//这里使用的是@ResponseBody,也就是循环完毕后,最终拿到的是RequestResponseBodyMethodProcessor处理器
if (handler.supportsReturnType(returnType)) {
return handler;
}
}
return null;
}
2)、结果拿到了RequestResponseBodyMethodProcessor
3)、handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
//相当于调用了RequestResponseBodyMethodProcessor.handleReturnValue方法【===这个是上面执行完毕后,找到的结果】
@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);
//即使返回值为空也要尝试
// Try even with null return value. ResponseBodyAdvice could get involved.
writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}
//writeWithMessageConverters
【这里面涉及内容协商】单独分析
2、返回值解析器原理writeWithMessageConverters
- 1、返回值处理器判断是否支持这种类型返回值 supportsReturnType
- 2、返回值处理器调用 handleReturnValue 进行处理
- 3、RequestResponseBodyMethodProcessor 可以处理返回值标了@ResponseBody 注解的。
-
- 1. 利用 MessageConverters 进行处理 将数据写为json
-
-
- 1、内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型)
- 2、服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据,
- 3、SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能处理?
-
-
-
-
- 1、得到MappingJackson2HttpMessageConverter可以将对象写为json
- 2、利用MappingJackson2HttpMessageConverter将对象转为json再写出去。
-
-
RequestResponseBodyMethodProcessor的writeWithMessageConverters方法,写基于消息转换器
问?消息转换器是如何转换的?
//根据请求获取可以接受的请求类型
List<MediaType> acceptableTypes = getAcceptableMediaTypes(request)
1)、ContentNegotiationManager
2)、contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request))找类型解析器
this.strategies = {ArrayList@6752} size = 2
0 = {ParameterContentNegotiationStrategy@6756}
1 = {HeaderContentNegotiationStrategy@6757}
3)、MediaType媒体类型ALL=*/*,支持所有,具体的可以这个里面查看
//返回可以生成的请求协议类型
List<MediaType> producibleTypes = getProducibleMediaTypes(request, valueType, targetType)
for (HttpMessageConverter<?> converter : this.messageConverters) {
//具体查看【默认的MessageConverter】解析
converter.canWrite(valueClass, null)//如果支持写result.addAll(converter.getSupportedMediaTypes());加进去
1.2、SpringMVC到底支持哪些返回值
ModelAndView Model View ResponseEntity ResponseBodyEmitter StreamingResponseBody HttpEntity HttpHeaders Callable DeferredResult ListenableFuture CompletionStage WebAsyncTask 有 @ModelAttribute 且为对象类型的 @ResponseBody 注解 ---> RequestResponseBodyMethodProcessor; 【上面有具体列出】
1.3、HTTPMessageConverter原理
1、MessageConverter规范
HttpMessageConverter: 看是否支持将 此 Class类型的对象,转为MediaType类型的数据。
例子:Person对象转为JSON。或者 JSON转为Person
2、默认的MessageConverter
messageConverters
result = {ArrayList@5053} size = 10
//converter.canWrite方法解析
//byte[].class == clazz,仅支持byte.class
0 = {ByteArrayHttpMessageConverter@6818}
//String.class == clazz
1 = {StringHttpMessageConverter@6819}
//跟上面一样的
2 = {StringHttpMessageConverter@6820}
//Resource.class这个类型
3 = {ResourceHttpMessageConverter@6821}
//DOMSource.class SAXSource.class StAXSource.class StreamSource.class Source.class
4 = {ResourceRegionHttpMessageConverter@6822}
//不是!MultiValueMap.class类型,
5 = {SourceHttpMessageConverter@6823}
//XmlRootElement有这个注解,方法上或者类上
6 = {AllEncompassingFormHttpMessageConverter@6824}
//MultiValueMap
7 = {MappingJackson2HttpMessageConverter@6825}
//true
8 = {MappingJackson2HttpMessageConverter@6826}
//支持注解方式xml处理的。
9 = {Jaxb2RootElementHttpMessageConverter@6827}
//xml转化
10 = {MappingJackson2XmlHttpMessageConverter@7408}
最终 MappingJackson2HttpMessageConverter 把对象转为JSON(利用底层的jackson的objectMapper转换的)
2、内容协商
根据客户端接收能力不同,返回不同媒体类型的数据。
1、引入xml依赖
jackson支持xml导入依赖
<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency>
2、postman分别测试返回json和xml
只需要改变请求头中Accept字段。Http协议中规定的,告诉服务器本客户端可以接收的数据类型。
3、开启浏览器参数方式内容协商功能
为了方便内容协商,开启基于请求参数的内容协商功能。
spring: contentnegotiation: favor-parameter: true #开启请求参数内容协商模式
发请求: http://localhost:8080/test/person?format=json
http://localhost:8080/test/person?format=xml
确定客户端接收什么样的内容类型;
1、Parameter策略优先确定是要返回json数据(获取请求头中的format的值)
2、最终进行内容协商返回给客户端json即可。
4、内容协商原理
- 1、判断当前响应头中是否已经有确定的媒体类型。MediaType
- 2、获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段)【application/xml】
-
- contentNegotiationManager 内容协商管理器 默认使用基于请求头的策略
- HeaderContentNegotiationStrategy 确定客户端可以接收的内容类型
- 3、遍历循环所有当前系统的 MessageConverter,看谁支持操作这个对象(Person)
- 4、找到支持操作Person的converter,把converter支持的媒体类型统计出来。
- 5、客户端需要【application/xml】。服务端能力【10种、json、xml】
- 6、进行内容协商的最佳匹配媒体类型
- 7、用 支持 将对象转为 最佳匹配媒体类型 的converter。调用它进行转化 。
导入了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()));
}
5、自定义 MessageConverter
实现多协议数据兼容。json、xml、x-guigu
0、@ResponseBody 响应数据出去 调用 RequestResponseBodyMethodProcessor 处理
1、Processor 处理方法返回值。通过 MessageConverter 处理
2、所有 MessageConverter 合起来可以支持各种媒体类型数据的操作(读、写)
3、内容协商找到最终的 messageConverter;
SpringMVC的什么功能。一个入口给容器中添加一个 WebMvcConfigurer
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
}
}
}
有可能我们添加的自定义的功能会覆盖默认很多功能,导致一些默认的功能失效。
大家考虑,上述功能除了我们完全自定义外?SpringBoot有没有为我们提供基于配置文件的快速修改媒体类型功能?怎么配置呢?【提示:参照SpringBoot官方文档web开发内容协商章节】