SpringBoot响应处理源码分析以及自定义MessageConverter

文章目录

  • 自定义pojo类通过@ResponseBody响应数据为json格式源码解析
  • 内容协商
  • 自定义MessageConverter

自定义pojo类通过@ResponseBody响应数据为json格式源码解析

1、RequestMappingHandlerAdapter.class
SpringBoot响应处理源码分析以及自定义MessageConverter_第1张图片
返回值处理器
SpringBoot响应处理源码分析以及自定义MessageConverter_第2张图片
执行目标方法
SpringBoot响应处理源码分析以及自定义MessageConverter_第3张图片
2、ServletInvocableHandlerMethod.class
确定参数,真正执行目标方法的地方,有方法的返回值。
SpringBoot响应处理源码分析以及自定义MessageConverter_第4张图片
3、InvocableHandlerMethod.class
获取当前目标方法所有参数的属性值
SpringBoot响应处理源码分析以及自定义MessageConverter_第5张图片
通过反射执行目标方法(也就是Controller方法)
SpringBoot响应处理源码分析以及自定义MessageConverter_第6张图片
4、UserController.java
进入目标方法
SpringBoot响应处理源码分析以及自定义MessageConverter_第7张图片

5、ServletInvocableHandlerMethod.class
利用返回值处理器处理返回值,把对象转换为json
SpringBoot响应处理源码分析以及自定义MessageConverter_第8张图片
6、HandlerMethodReturnValueHandlerComposite.class
找到支持的返回值处理器
SpringBoot响应处理源码分析以及自定义MessageConverter_第9张图片
挨个判断所有返回值处理器哪个支持处理目标方法的返回值
SpringBoot响应处理源码分析以及自定义MessageConverter_第10张图片
找到支持的返回值处理器调用 handleReturnValue 方法进行处理
SpringBoot响应处理源码分析以及自定义MessageConverter_第11张图片
7、RequestResponseBodyMethodProcessor.class
RequestResponseBodyMethodProcessor可以处理返回值标了@ResponseBody注解的,利用MessageConverters进行处理将数据写为json
SpringBoot响应处理源码分析以及自定义MessageConverter_第12张图片
8、AbstractMessageConverterMethodProcessor.class
内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型)
SpringBoot响应处理源码分析以及自定义MessageConverter_第13张图片
注:自定义的格式不会出现在请求头里面,而是要通过基于请求参数的策略
SpringBoot响应处理源码分析以及自定义MessageConverter_第14张图片
服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据,
SpringBoot响应处理源码分析以及自定义MessageConverter_第15张图片
SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁可以将对象写为json
SpringBoot响应处理源码分析以及自定义MessageConverter_第16张图片
HttpMessageConverter: 看是否支持将 此 Class类型的对象,转为MediaType类型的数据。
例:Book对象转为JSON。或者 JSON转为Book
SpringBoot响应处理源码分析以及自定义MessageConverter_第17张图片
将数据写出去
SpringBoot响应处理源码分析以及自定义MessageConverter_第18张图片
9、AbstractGenericHttpMessageConverter.class
将数据写给响应
SpringBoot响应处理源码分析以及自定义MessageConverter_第19张图片
10、AbstractJackson2HttpMessageConverter.class
将对象转换为json写出去
SpringBoot响应处理源码分析以及自定义MessageConverter_第20张图片
11、AbstractGenericHttpMessageConverter.class
最终所有容器底层的 HttpMessageConverter中的MappingJackson2HttpMessageConverter把对象转为JSON形式写出去
SpringBoot响应处理源码分析以及自定义MessageConverter_第21张图片

内容协商

1、引入xml依赖

<dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
</dependency>

2、yaml配置文件开启浏览器参数方式内容协商功能,开启之后在浏览器输入http://localhost:8081/responseData?format=xml 即可开启基于xml的请求参数

spring:
	mvc:
		contentnegotiation:
			favor-parameter: true  #开启请求参数内容协商模式

注意:使用上述浏览器url方式请求参数是基于参数的策略(ParameterContentNegotiationStrategy),使用下面postman请求参数是基于请求头的策略(HeaderContentNegotiationStrategy)
3、postman分别测试返回json和xml
SpringBoot响应处理源码分析以及自定义MessageConverter_第22张图片
4、内容协商原理
1)获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段)【application/xml】。

List acceptableTypes = this.getAcceptableMediaTypes( request);

注:getAcceptableMediaTypes方法底层使用了contentNegotiationManager内容协商管理器,默认使用基于请求头的策略。也就是ContentNegotiationStrategy的实现类HeaderContentNegotiationStrategy来确定客户端可以接收的内容类型
SpringBoot响应处理源码分析以及自定义MessageConverter_第23张图片

SpringBoot响应处理源码分析以及自定义MessageConverter_第24张图片

2)遍历循环所有当前系统的 MessageConverter,看谁支持操作这个对象,把converter支持的媒体类型统计出来。客户端需要【application/xml】。服务端能力【10种】

List producibleTypes = this.getProducibleMediaTypes(request,valueType,(Type)tangetType);

SpringBoot响应处理源码分析以及自定义MessageConverter_第25张图片
3)进行内容协商的最佳匹配媒体类型,用支持将对象转为最佳匹配媒体类型的converter,调用它进行转化 。

Iterator var23 = this.messageConverters.iterator();
while(var23.hasNext()) {
	converter = (HttpMessageConverter)var23.next();
	genericConverter = converter instanceof GenericHttpMessageConverter ? (GenericHttpMessageConverter)converter : null;
	if (genericConverter != null) {
		if (((GenericHttpMessageConverter)converter).canWrite((Type)targetType, valueType, selectedMediaType)) {
			break label183;
		}
	} else if (converter.canWrite(valueType, selectedMediaType)) {
		break label183;
	}
}

SpringBoot响应处理源码分析以及自定义MessageConverter_第26张图片

自定义MessageConverter

1、自定义消息订制器类MyMessageConvert实现HttpMessageConverter接口

public class MyMessageConvert implements HttpMessageConverter<Book> {
    @Override
    public boolean canRead(Class<?> clazz, MediaType mediaType) {
        return false;
    }

    @Override
    public boolean canWrite(Class<?> clazz, MediaType mediaType) {
        return clazz.isAssignableFrom(Book.class);
    }

    @Override
    public List<MediaType> getSupportedMediaTypes() {
        return MediaType.parseMediaTypes("application/x-myForm");
    }

    @Override
    public Book read(Class<? extends Book> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }

    @Override
    public void write(Book book, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        String data = book.getBookId() + ";" + book.getBookName() + ";" + book.getPrice() + ";" + book.getStock();
        OutputStream body = outputMessage.getBody();
        body.write(data.getBytes());
    }
}

2、WebMvcConfigurer接口有两个相关方法,configureMessageConverters是覆盖默认消息订制器

extendMessageConverters是追加消息订制器。我们自定义消息订制器需要实现extendMessageConverters方法SpringBoot响应处理源码分析以及自定义MessageConverter_第27张图片

@Component
public class MyConfig {

    @Bean
    public WebMvcConfigurer myWebMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
                converters.add(new MyMessageConvert());
            }

            @Override
            public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
                Map<String, MediaType> mediaTypes = new HashMap<>();
                mediaTypes.put("xml", MediaType.APPLICATION_XML);
                mediaTypes.put("json", MediaType.APPLICATION_JSON);
                mediaTypes.put("myForm", MediaType.parseMediaType("application/x-myForm"));
                ParameterContentNegotiationStrategy parameterStrategy = new ParameterContentNegotiationStrategy(mediaTypes);
//                HeaderContentNegotiationStrategy headStrategy = new HeaderContentNegotiationStrategy();
                configurer.strategies(Arrays.asList(parameterStrategy));
            }
        };
    }
}

注意:因为是自定义消息订制器,通过浏览器url请求参数,所以contentNegotiationManager内容协商管理器是ParameterContentNegotiationStrategy基于参数策略,但它只有xml和json两种格式,所以要实现configureContentNegotiation方法增加自定义格式。但是该方法慎用!因为它会覆盖默认很多功能,导致一些默认的功能失效。
例:
实现configureContentNegotiation之前,通过postman访问正常,浏览器访问异常
SpringBoot响应处理源码分析以及自定义MessageConverter_第28张图片
实现configureContentNegotiation之后,基于请求头的策略消失了,只剩下基于参数的。此时浏览器访问虽然正常,但是postman解析处理的acceptableTypes的值为"*/*",对于任何类型的格式都匹配,风险较高!因此configureContentNegotiation方法里需要通过new HeaderContentNegotiationStrategy()来增加请求头策略
SpringBoot响应处理源码分析以及自定义MessageConverter_第29张图片

3、配置yaml文件

spring:
	mvc:
		contentnegotiation:
			favor-parameter: true  #开启请求参数内容协商模式

4、为客户端提供访问接口:localhost:8081/responseData?format=myForm

@Controller
public class UserController {
    @Autowired
    private UserService userService;


    @ResponseBody
    @RequestMapping(value = "/requestBook", method = RequestMethod.POST)
    public Book requestBook(Book book){
        return book;
    }

    @ResponseBody
    @RequestMapping(value = "/responseData", method = RequestMethod.GET)
    public Book responseData(){
        Book book = new Book();
        book.setBookId(1);
        book.setBookName("三体");
        book.setPrice(100);
        book.setStock(10);
        return book;
    }
}

你可能感兴趣的:(SpringBoot,spring,boot,java)