引言:如何在SpringMVC中统一对返回的Json进行加密?”。

postHandler方法处理。实际这是行不通的,因为当程序运行到该方法,是在返回数据之后,渲染页面之前,所以这时候HttpServletResponse中的输出流已经关闭了,自然无法在对返回数据进行处理。MethodArgumentResolverHandlerMethodReturnValueHandler,还是接下来要提到的HttpMessageConverter@RequestBody@ResponseBody,通过这两个注解,可以在Controller中直接使用Java对象作为请求参数和返回内容,而完成这之间转换作用的便是HttpMessageConverter


springmvc简单接口 springmvc常用接口_springmvc简单接口


springmvc简单接口 springmvc常用接口_xml_02

package org.springframework.http.converter;

 
 
 

import java.io.IOException;

 

import java.util.List;

 
 
 

import org.springframework.http.HttpInputMessage;

 

import org.springframework.http.HttpOutputMessage;

 

import org.springframework.http.MediaType;

 
 
 

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;

 
 
 
}

HttpMessageConverter接口提供了5个方法:canRead

  • :判断该转换器是否能将请求内容转换成Java对象

canWrite

  • :判断该转换器是否可以将Java对象转换成返回内容

getSupportedMediaTypes

  • :获得该转换器支持的MediaType类型

read

  • :读取请求内容并转换成Java对象

write

  • :将Java对象转换后写入返回内容

readwrite方法的参数分别有有HttpInputMessageHttpOutputMessage对象,这两个对象分别代表着一次Http通讯中的请求和响应部分,可以通过getBody方法获得对应的输入流和输出流。




springmvc简单接口 springmvc常用接口_spring_03


springmvc简单接口 springmvc常用接口_spring mvc_04

当前SpringMVC中已经默认提供了相当多的转换器,如上图,其中常用的有:

名称

作用

读支持MediaType

写支持MediaType

ByteArrayHttpMessageConverter

数据与字节数组的相互转换

*/*

application/octet-stream

StringHttpMessageConverter

数据与String类型的相互转换

text/*

text/plain

FormHttpMessageConverter

表单与MultiValueMap的相互转换

application/x-www-form-urlencoded

application/x-www-form-urlencoded

SourceHttpMessageConverter

数据与javax.xml.transform.Source的相互转换

text/xml和application/xml

text/xml和application/xml

MarshallingHttpMessageConverter

使用Spring的Marshaller/Unmarshaller转换XML数据

text/xml和application/xml

text/xml和application/xml

MappingJackson2HttpMessageConverter

使用Jackson的ObjectMapper转换Json数据

application/json

application/json

MappingJackson2XmlHttpMessageConverter

使用Jackson的XmlMapper转换XML数据

application/xml

application/xml

BufferedImageHttpMessageConverter

数据与java.awt.image.BufferedImage的相互转换

Java I/O API支持的所有类型

Java I/O API支持的所有类型


springmvc简单接口 springmvc常用接口_spring_05


SpringMVC自己的 

HttpMessageConverter还是定义在RequestMappingHandlerAdapter里,并且被MethodArgumentResolverHandlerMethodReturnValueHandler解析数据的时候所用到。


public RequestMappingHandlerAdapter() {

 

    StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();

 

    stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316

 
 
 

    this.messageConverters = new ArrayList<HttpMessageConverter<?>>(4);

 

    this.messageConverters.add(new ByteArrayHttpMessageConverter());

 

    this.messageConverters.add(stringHttpMessageConverter);

 

    this.messageConverters.add(new SourceHttpMessageConverter<Source>());

 

    this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());

 
}


这里面四个

HttpMessageConverter

,重点介绍下 AllEncompassingFormHttpMessageConverter

,看源码。

public class AllEncompassingFormHttpMessageConverter extends FormHttpMessageConverter {

 
 
 

    private static final boolean jaxb2Present =

 

            ClassUtils.isPresent("javax.xml.bind.Binder", AllEncompassingFormHttpMessageConverter.class.getClassLoader());

 
 
 

    private static final boolean jackson2Present =

 

            ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", AllEncompassingFormHttpMessageConverter.class.getClassLoader()) &&

 

                    ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", AllEncompassingFormHttpMessageConverter.class.getClassLoader());

 
 
 

    private static final boolean jackson2XmlPresent =

 

            ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", AllEncompassingFormHttpMessageConverter.class.getClassLoader());

 
 
 

    private static final boolean gsonPresent =

 

            ClassUtils.isPresent("com.google.gson.Gson", AllEncompassingFormHttpMessageConverter.class.getClassLoader());

 
 
 
 
 

    public AllEncompassingFormHttpMessageConverter() {

 

        addPartConverter(new SourceHttpMessageConverter<Source>());

 
 
 

        if (jaxb2Present && !jackson2XmlPresent) {

 

            addPartConverter(new Jaxb2RootElementHttpMessageConverter());

 

        }

 
 
 

        if (jackson2Present) {

 

            addPartConverter(new MappingJackson2HttpMessageConverter());

 

        } else if (gsonPresent) {

 

            addPartConverter(new GsonHttpMessageConverter());

 

        }

 
 
 

        if (jackson2XmlPresent) {

 

            addPartConverter(new MappingJackson2XmlHttpMessageConverter());

 

        }

 

    }

 
 
 
}



它的主要作用是,从类加载器去查找相关的类,只有这些转换器需要的类(jar包被你引入了)存在,那么转换器才会被加载进去,比如我们在项目中引入了

    1. <dependency>
    2.     <groupId>com.fasterxml.jackson.core</groupId>
    3.     <artifactId>jackson-databind</artifactId>
    4.     <version>${jackson.version}</version>
    5. </dependency>



    MappingJackson2HttpMessageConverter被加入到了SpringMVC中,后面在使用@RequestBody@ResponseBodyRequestResponseBodyMethodProcessor起作用的时候,它就会寻找能够匹配的Converter,进而找到MappingJackson2HttpMessageConverter用来转换对象。

    那如果我们引入了

      1. <dependency>
      2.     <groupId>com.fasterxml.jackson.dataformat</groupId>
      3.     <artifactId>jackson-dataformat-xml</artifactId>
      4.     <version>${jackson.version}</version>
      5. </dependency>

      那也就预示着MappingJackson2XmlHttpMessageConverter被加入转换器队列中了,那么你使用@ResponseBody,响应类型为Content-Type: application/xml;charset=UTF-8,估计是默认请求头Accept直接为application/xml,具体源码我就不去翻了。

      springmvc简单接口 springmvc常用接口_xml_06

      当用户发送请求后,@RequestBody注解会读取请求body中的数据,通过获取请求头Header中的Content-Type来确认请求头的数据格式,从而来为请求数据适配合适的转换器。例如contentType:applicatin/json,那么转换器会适配MappingJackson2HttpMessageConverter。响应时候的时候同理,@ResponseBody注解会通过检测请求头HeaderAccept属性来适配对应响应的转换器。

      springmvc简单接口 springmvc常用接口_spring_07


      springmvc简单接口 springmvc常用接口_xml_08

      我这里因为没找到合适的例子去实现自己的HttpMessageConverter,但想到了另外一个可以拿来用的例子。

      @RequestMapping(value="simple")
      
       
      
      public String simple(){
      
       
      
          return "abc中国";
      
       
      }

      abc中国.jsp的页面,由于找不到,会报404

      但当我们稍微修改如下:

      @RequestMapping(value="simple")
      
       
      @ResponseBody
       
      
      public String simple(){
      
       
      
           return "abc中国";
      
       
      
       }

      @ResponseBody,当我们再次发起请求的时候,请求头Header中Accept为 

      text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8StringHttpMessageConverter,并完成转换,输出的直接是返回值,然而我们却发现结果中文乱码了,这是因为该类默认支持的编码格式是ISO-8859-1

      package org.springframework.http.converter;
      
       
       
       
      
      import java.io.IOException;
      
       
      
      import java.io.UnsupportedEncodingException;
      
       
      
      import java.nio.charset.Charset;
      
       
      
      import java.util.ArrayList;
      
       
      
      import java.util.List;
      
       
       
       
      
      import org.springframework.http.HttpInputMessage;
      
       
      
      import org.springframework.http.HttpOutputMessage;
      
       
      
      import org.springframework.http.MediaType;
      
       
      
      import org.springframework.util.StreamUtils;
      
       
       
       
       
       
      
      public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> {
      
       
       
       
      
          public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1");

      那我们应当如何使之支持UTF-8呢,这也是接下来我们所需要介绍的,应当如何覆盖SpringMVC自己的转换器,或者新增自定义的转换器。


        1. <mvc:annotation-driven>
        2.     <mvc:message-converters>
        3.         <bean class="org.springframework.http.converter.StringHttpMessageConverter">
        4.             <property name="supportedMediaTypes">
        5.                 <list>
        6.                     <value>text/plain;charset=UTF-8</value>
        7.                     <value>text/html;charset=UTF-8</value>
        8.                 </list>
        9.              </property>
        10.         </bean>
        11.     </mvc:message-converters>
        12. </mvc:annotation-driven>



        如果我们不想使用系统默认的转换器,还可以去掉系统给你定义的,然后重新定义你所需要的转换器,如下:


          1. <mvc:annotation-driven>
          2.     <mvc:message-converters register-defaults="false">
          3.         <bean class="org.springframework.http.converter.StringHttpMessageConverter">
          4.             <property name="supportedMediaTypes">
          5.                 <list>
          6.                     <value>text/plain;charset=UTF-8</value>
          7.                     <value>text/html;charset=UTF-8</value>
          8.                 </list>
          9.              </property>
          10.          </bean>
          11.     </mvc:message-converters>
          12. </mvc:annotation-driven>


          我个人的意见是,如果没有特别的需要,不要重新定义自己的转换器,我们大可覆盖或者新增就行了。