zoukankan      html  css  js  c++  java
  • SpringMVC源码分析(数据转换和格式化)

    @author:QYX

    本系列共30章,争取大家读完后,都能自己手写实现一下属于自己的SpringMVC框架

    因为写于很久之前,所以版本可能较老,请见谅

    当一个请求到达DispatcherServlet的时候,需要找到对应的HandlerMapping,然后根据HandlerMapping去找对应的HandlerAdapter执行处理器,处理器在要调用控制器之前,需要先获取HTTP发送过来的信息,然后将其转变为控制器的各种不同类型参数,这就是各类注解能够得到丰富类型参数的原因。它首先用HTTP的消息转换器(HTTPMessageConverter)对消息转换,但是这是一个比较原始的转换,他是类型和文件类型比较简易的转换,他还需要进一步转换才能转换为POJO或者其他丰富的参数类型。

    当处理器处理完了这些参数的转换,它就会进行验证,当控制器完成了对应的逻辑,返回结果后,处理器如果可以找到对应处理结果类型的HTTPMessageConverter的实现类,它就会调用对应的HTTPMessageConverter的实现类方法。

    对于SpringMVC,在XML配置了<mvc:annotation-driven>,或者Java配置的注解上加入@EnableWebMvc的时候,SpringIOC容器会自定义生成一个关于转换器和格式化器的类实例-------FormattingConverterFactoryBean,这样就可以从Spring IoC容器中获取这个对象了,它的产品主要就是DefaultFormattingConversionService类对象。

    它的顶层接口-------ConversionService接口,它还实现了转换器的注册器(ConverterRegister)和格式化器(FormatterRegitry)两个接口,也就是说可以在它那注册转换器或者格式化器了。

    在运行控制器之间,他就会把这些转换器把HTTP的数据转换为对应的类型,用以填充控制器的参数,这就是为什么在控制器保持一定规则下就能够得到参数的原因。

    在Java类型转换之前,在SpringMVC中,为了应对了HTTP请求,它还定义了HttpMessageConverter,它是一个总体的接口,通过它可以读入HTTP的请求内容, 也就是说,在读取HTTP请求的参数和内容的时候会先用HttpMessageConverter读出,做一次简单转换为Java类型,主要是字符串(String),然后就可以使用各类转换器进行转换了,在逻辑业务处理完成后,还可以通过它把数据转换为响应给用户的内容

    对于转换器而言,Spring分为两大类,一种是Converter接口所定义的,另外一种GenericConverter,它们都可以使用注册机注册,它们都是来自于Spring Core项目,而非Spring MVC项目,他的作用范围是Java内部各种类型之间的转换

    HttpMessageConverter实例

    HttpMessageConverter是定义从HTTP接口请求信息和应答给用户的

    public interface HttpMessageConverter<T> {  
      
        /** 
         * Indicates whether the given class can be read by this converter. 
         * @param clazz the class to test for readability 
         * @param mediaType the media type to read, can be {@code null} if not specified. 
         * Typically the value of a {@code Content-Type} header. 
         * @return {@code true} if readable; {@code false} otherwise 
         */  
        boolean canRead(Class<?> clazz, MediaType mediaType);  
      
        /** 
         * Indicates whether the given class can be written by this converter. 
         * @param clazz the class to test for writability 
         * @param mediaType the media type to write, can be {@code null} if not specified. 
         * Typically the value of an {@code Accept} header. 
         * @return {@code true} if writable; {@code false} otherwise 
         */  
        boolean canWrite(Class<?> clazz, MediaType mediaType);  
      
        /** 
         * Return the list of {@link MediaType} objects supported by this converter. 
         * @return the list of supported media types 
         */  
        List<MediaType> getSupportedMediaTypes();  
      
        /** 
         * Read an object of the given type form the given input message, and returns it. 
         * @param clazz the type of object to return. This type must have previously been passed to the 
         * {@link #canRead canRead} method of this interface, which must have returned {@code true}. 
         * @param inputMessage the HTTP input message to read from 
         * @return the converted object 
         * @throws IOException in case of I/O errors 
         * @throws HttpMessageNotReadableException in case of conversion errors 
         */  
        T read(Class<? extends T> clazz, HttpInputMessage inputMessage)  
                throws IOException, HttpMessageNotReadableException;  
      
        /** 
         * Write an given object to the given output message. 
         * @param t the object to write to the output message. The type of this object must have previously been 
         * passed to the {@link #canWrite canWrite} method of this interface, which must have returned {@code true}. 
         * @param contentType the content type to use when writing. May be {@code null} to indicate that the 
         * default content type of the converter must be used. If not {@code null}, this media type must have 
         * previously been passed to the {@link #canWrite canWrite} method of this interface, which must have 
         * returned {@code true}. 
         * @param outputMessage the message to write to 
         * @throws IOException in case of I/O errors 
         * @throws HttpMessageNotWritableException in case of conversion errors 
         */  
        void write(T t, MediaType contentType, HttpOutputMessage outputMessage)  
                throws IOException, HttpMessageNotWritableException;  
      
    }  

    源码分析:

    canRead:判断类型是否可读,clazz是类别,而对于mediaType是Http类型

    canWrite:判断类型是否可写,clazz是类别,而对于mediaType是Http类型

    getSupportedMediaTypes:对于mediaType是HTTP的类型

    read:读取数据类型,进行转换,clazz是类,而inputMessage是HTTP请求消息

    write:消息写,contentType是HTTP类型,outputMessage是HTTP的应答消息

    拿我们经常使用的MappingJacksonHttpMessageConverter举例,他的配置如下:

    MappingJacksonHttpMessageConverter 用于将对象转换为 JSON,反之亦然。此内置转换程序使用 Jackson 的 ObjectMapper 将 JSON 映射到 JavaBean,因此您必须将下列 Jackson JAR 文件添加到类路径。 
    org.codehaus.jackson.jar 
    org.codehaus.jackson.mapper.jar

    他的测试调用很简单,可以使用@ResponseBody返回Json格式的数据

    @ResponseBody 注释用于将返回对象(Employee 或 EmployeeList)变为响应的正文内容,将使用 MappingJacksonHttpMessageConverter 将其映射到 JSON。

    使用 HttpMessageConverter 和 @ResponseBody,您可以实现多个具象,而无需包含 Spring 的视图技术 — 这是使用 ContentNegotiatingViewResolver 所不具有的一个优势。

    Spring MVC的@ResponseBody 的作用是把返回值直接写到HTTP response body里。 使用AnnotationMethodHandlerAdapter的handleResponseBody方法, AnnotationMethodHandlerAdapter使用request header中”Accept”的值和messageConverter支持的MediaType进行匹配,然后会用”Accept”的第一个值写入 response的”Content-Type”。

    AnnotationMethodHandlerAdapter将会初始化7个转换器,可以通过调用AnnotationMethodHandlerAdapter的getMessageConverts()方法来获取转换器的一个集合

    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
       <property name="messageConverters">
           <list>
               <ref bean="jsonConverter" />
               <ref bean="marshallingConverter" />
               <ref bean="atomConverter" />
           </list>
       </property>
    </bean>
    
    <bean id="jsonConverter"   class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
       <property name="supportedMediaTypes" value="application/json" />
    </bean>
  • 相关阅读:
    Python基础学习参考(四):条件与循环
    Python基础学习参考(三):内置函数
    Python基础学习参考(二):基本语法
    2011的最后一篇博文 写给我自己也写给你们
    前端开发面试题
    常用js操作:
    两个嵌套for循环执行顺序
    在寻找学习js的途中,又发现了好的东西!
    arcgis api for js初学
    解决为什么arcgis api for js的first map里不显示arcgis地图
  • 原文地址:https://www.cnblogs.com/qyx66/p/12315229.html
Copyright © 2011-2022 走看看