美文网首页撸一个mvc框架
SpringMVC 源码分析 -- HttpMessageCon

SpringMVC 源码分析 -- HttpMessageCon

作者: 想54256 | 来源:发表于2020-11-12 17:19 被阅读0次

    title: SpringMVC 源码分析 -- HttpMessageConverter
    date: 2020/11/06 09:42


    引言

    在项目中遇到了类似下面这样的代码,就比较好奇到底返回给前端的是文件流还是那个地址,并且跟下源码看看底层到底是怎么处理的。

    @RestController
    @SpringBootApplication
    public class TestReponseApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(TestReponseApplication.class, args);
        }
    
    
        @RequestMapping
        public String func(HttpServletResponse response) throws Exception {
    
            response.setContentType("application/octet-stream");
            response.addHeader("Content-Disposition", "attachment; filename=aaa.jpg");
            response.addHeader("Cache-Control", "no-cache");
            FileInputStream in = new FileInputStream(new File("/Users/x5456/Downloads/1501994358.jpg"));
            StreamUtils.copy(in, response.getOutputStream());
    
            // 此时文件的所有字节全部传输到了前端,但是连接还没关闭
    
            return "http://123.jpg";
        }
    
    }
    

    测试的结果是返回了文件流,但是好奇http://123.jpg这个返回信息跑哪去了,是 SpringMVC 看到 response 已经写入了,所以就将其抛弃了?但是看了源码之后发现好像并没有这个步骤,它是直接将http://123.jpg继续写进了response 中,经过下载下来的文件对比也证明了这一点。

    image

    HttpMessageConverter

    public interface HttpMessageConverter<T> {
    
        /**
         * 表明是否该消息转化器可以(从 request)读出给定的 class 的对象
         *
         * @param clazz     要测试可读性的类
         * @param mediaType 要被读取的 MediaType 如果未指定为null,通常这个值是请求头中的 content-type
         * @return 如果可读返回true,否则是false
         */
        boolean canRead(Class<?> clazz, MediaType mediaType);
    
        /**
         * 表明是否该消息转化器可以(向 response)写入给定 class 的对象
         *
         * @param clazz     要测试可写性的类
         * @param mediaType 要被读取的 MediaType 如果未指定为null,通常这个值是请求头中的Accept
         * @return 如果可写返回true,否则是false
         */
        boolean canWrite(Class<?> clazz, MediaType mediaType);
    
        /**
         * @return 返回这个消息转换器可以支持的MediaType的集合
         */
        List<MediaType> getSupportedMediaTypes();
    
        /**
         * 从给出的input message中读出给定类型的对象 然后返回
         *
         * @param clazz        返回对象的类型. 这个类必须支持前面的canRead方法且返回true
         * @param inputMessage the HTTP input message to read from
         * @return 被转换的对象
         */
        T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
                throws IOException, HttpMessageNotReadableException;
    
        /**
         * 将给定的对象写入给定的输出消息
         *
         * @param t             要写入输出消息的对象. 这个类型必须通过了前面的canWrite方法且返回true
         * @param contentType   编写时使用的内容类型. 可能是{@code null}来表示必须使用转换器的默认内容类型。如果不是{@code null},则此媒体类型必须调用过这个接口的{@link #canWrite canWrite}方法且必须返回{@code true}。
         * @param outputMessage the message to write to
         */
        void write(T t, MediaType contentType, HttpOutputMessage outputMessage)
                throws IOException, HttpMessageNotWritableException;
    }
    

    Http报头Accept与Content-Type的区别?

    1、Accept属于请求头, Content-Type属于实体头。

    Http报头分为通用报头、请求报头、响应报头和实体报头。

    请求方的http报头结构:通用报头|请求报头|实体报头

    响应方的http报头结构:通用报头|响应报头|实体报头

    2、Accept代表发送端(客户端)希望接受的数据类型。

    比如:Accept:text/xml;

    代表客户端希望接受的数据类型是xml类型

    3、Content-Type代表发送端(客户端|服务器)发送的实体数据的数据类型。

    比如:Content-Type:text/html;

    代表发送端发送的数据格式是html。

    ???为啥读方法不需要 MediaType

    在读写操作方面springMVC又提供了读操作接口HttpInputMessage和写操作接口HttpOutputMessage来完成数据的读写操作。

    public interface HttpInputMessage extends HttpMessage {
     
        // 底层实现 request.getInputStream();
        InputStream getBody() throws IOException;
    }
    
    public interface HttpOutputMessage extends HttpMessage {
    
        // 底层实现 response.getOutputStream();
        OutputStream getBody() throws IOException;
    }
    
    // 父接口
    public interface HttpMessage {
    
        //获取头部中的信息
        HttpHeaders getHeaders(); 
    }
    

    read 流程

    我们从这篇文章的 tag4 的第三张图片继续往下讲,因为那上面讲的是 get 请求的参数解析器,没有用到 HttpMessageConverter,下面讲使用了 @RequestBody 注解所采用的参数解析器。

    image

    注:RequestResponseBodyMethodProcessor 校验是否能解析当前参数

    image image image image image

    我们接下来看下这三个方法吧,不写注释应该也能看懂,Advice 是通知的意思,所以他的作用就是在使用 @RequestBody 接收参数的前后做一些事情。

    public interface RequestBodyAdvice {
    
    
        boolean supports(MethodParameter methodParameter, Type targetType,
                Class<? extends HttpMessageConverter<?>> converterType);
    
        HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,
                Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException;
    
        Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
                Type targetType, Class<? extends HttpMessageConverter<?>> converterType);
    
        @Nullable
        Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,
                Type targetType, Class<? extends HttpMessageConverter<?>> converterType);
    
    
    }
    

    其中默认有一个 JsonView 相关的 RequestAdvice,@JsonView 介绍

    image

    问题

    1. HttpMessageConverter 是怎么创建出来的?
    image image

    之后又被 @EnableMvc 注解引入的配置类中的方法覆盖掉了(不过无所谓了)。

    1. 只有部分参数解析器需要 HttpMessageConverter,那么是怎么注入进去的呢
    image image

    write 流程

    我们从这篇文章的 tag5 的最后一张图片开始讲起。

    image image image image image image

    因为他是直接写入流中的,没有管流里是否已经存在了,所以会出现下面这种情况:

    image

    常用HttpMessageConverter与媒体类型的对应信息:

    HttpMessageConverter 处理哪些格式的数据
    StringHttpMessageConverter 读取字符串格式的数据和写出字符串式的数据
    ByteArrayHttpMessageConverter 读取二进制格式的数据和写出二进制格式的数据
    FormHttpMessageConverter 负责读取form提交的数据(能读取的数据格式为 application/x-www-form-urlencoded,不能读取multipart/form-data格式数据);负责写入application/x-www-from-urlencoded和multipart/form-data格式的数据
    ResourceHttpMessageConverter 负责读取资源文件和写出资源文件数据
    Jaxb2RootElementHttpMessageConverter 读取和写入xml 标签格式的数据
    ResourceHttpMessageConverter 负责读取资源文件和写出资源文件数据
    MappingJacksonHttpMessageConverter 读取和写入json格式的数据

    ???MVC中的适配器作用是啥,他到底适配了什么

    相关文章

      网友评论

        本文标题:SpringMVC 源码分析 -- HttpMessageCon

        本文链接:https://www.haomeiwen.com/subject/llmpbktx.html