Spring-MVC配置Gson做为Message Converter解析Json

在学习Spring的时候看到可以使用@RequestBody 和@ResponseBody注解来是的Spring自动将http 其中的 body(json格式)部分和java内部的类进行转换。同时由于Google Gson的强大一般开发的时候会用的比较多,但是由于Spring内部默认使用的json的message Converter 并不是gson,所以这里需要配置一下才能使其生效;

Spring其实已经在实现了对gson的支持,但是如果想在项目中使用,还需要通过配置一下才可以:

配置文件如下:

    <!--<mvc:annotation-driven/>-->
<mvc:annotation-driven>
      <mvc:message-converters>
          <bean class="org.springframework.http.converter.json.GsonHttpMessageConverter"/>
      </mvc:message-converters>
  </mvc:annotation-driven>

其中org.springframework.http.converter.json.GsonHttpMessageConverter是Spring已经实现了的,在spring的包中能找到这个类。

注:如果想要某中类型的数据能被Message Converter转换,需要自己实现AbstractGenericHttpMessageConverter这个接口,然后在通过配置文件将这个类加载进你的项目中(比如说alibaba:fastjson,在它包中也实现了度spring message converter 的支持)。

备注: 网上有很多实现自己的Message Converter的教程,如果后续需要可以搜一下,实现起来有两个步骤:

  1. 需要自己实现AbstractGenericHttpMessageConverter这个接口;

  2. 通过xml或者其他方法配置一下

    <mvc:annotation-driven>
      <mvc:message-converters>
          <bean class="org.springframework.http.converter.json.GsonHttpMessageConverter"/>
      </mvc:message-converters>
    </mvc:annotation-driven>

    #在pom下加入依赖
    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.3.1</version>
    </dependency>
    @Configuration
    @ConditionalOnClass(Gson.class)
    @ConditionalOnMissingClass(name = "com.fasterxml.jackson.core.JsonGenerator")
    @ConditionalOnBean(Gson.class)
    protected static class GsonHttpMessageConverterConfiguration {

      @Bean
      @ConditionalOnMissingBean
      public GsonHttpMessageConverter gsonHttpMessageConverter(Gson gson) {
          GsonHttpMessageConverter converter = new GsonHttpMessageConverter();
          converter.setGson(gson);
          return converter;
      }

    }

    <dependency>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.3.1</version>
    </dependency>
  3. 一下贴点源码:

    package org.springframework.http.converter.json;

    import com.google.gson.Gson;
    import com.google.gson.JsonIOException;
    import com.google.gson.JsonParseException;
    import com.google.gson.reflect.TypeToken;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.lang.reflect.Type;
    import java.nio.charset.Charset;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpInputMessage;
    import org.springframework.http.HttpOutputMessage;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.AbstractGenericHttpMessageConverter;
    import org.springframework.http.converter.HttpMessageNotReadableException;
    import org.springframework.http.converter.HttpMessageNotWritableException;
    import org.springframework.util.Assert;

    public class GsonHttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {
      public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
      private Gson gson = new Gson();
      private String jsonPrefix;

      public GsonHttpMessageConverter() {
          super(new MediaType[]{MediaType.APPLICATION_JSON, new MediaType("application", "*+json")});
          this.setDefaultCharset(DEFAULT_CHARSET);
      }

      public void setGson(Gson gson) {
          Assert.notNull(gson, "'gson' is required");
          this.gson = gson;
      }

      public Gson getGson() {
          return this.gson;
      }

      public void setJsonPrefix(String jsonPrefix) {
          this.jsonPrefix = jsonPrefix;
      }

      public void setPrefixJson(boolean prefixJson) {
          this.jsonPrefix = prefixJson?")]}', ":null;
      }

      public boolean canRead(Class<?> clazz, MediaType mediaType) {
          return this.canRead(mediaType);
      }

      public boolean canWrite(Class<?> clazz, MediaType mediaType) {
          return this.canWrite(mediaType);
      }

      protected boolean supports(Class<?> clazz) {
          throw new UnsupportedOperationException();
      }

      protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
          TypeToken<?> token = this.getTypeToken(clazz);
          return this.readTypeToken(token, inputMessage);
      }

      public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
          TypeToken<?> token = this.getTypeToken(type);
          return this.readTypeToken(token, inputMessage);
      }

      protected TypeToken<?> getTypeToken(Type type) {
          return TypeToken.get(type);
      }

      private Object readTypeToken(TypeToken<?> token, HttpInputMessage inputMessage) throws IOException {
          InputStreamReader json = new InputStreamReader(inputMessage.getBody(), this.getCharset(inputMessage.getHeaders()));

          try {
              return this.gson.fromJson(json, token.getType());
          } catch (JsonParseException var5) {
              throw new HttpMessageNotReadableException("Could not read JSON: " + var5.getMessage(), var5);
          }
      }

      private Charset getCharset(HttpHeaders headers) {
          return headers != null && headers.getContentType() != null && headers.getContentType().getCharset() != null?headers.getContentType().getCharset():DEFAULT_CHARSET;
      }

      protected void writeInternal(Object o, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
          Charset charset = this.getCharset(outputMessage.getHeaders());
          OutputStreamWriter writer = new OutputStreamWriter(outputMessage.getBody(), charset);

          try {
              if(this.jsonPrefix != null) {
                  writer.append(this.jsonPrefix);
              }

              if(type != null) {
                  this.gson.toJson(o, type, writer);
              } else {
                  this.gson.toJson(o, writer);
              }

              writer.close();
          } catch (JsonIOException var7) {
              throw new HttpMessageNotWritableException("Could not write JSON: " + var7.getMessage(), var7);
          }
      }
    }
  4. 然后是alibaba:fastjson的实现

    package com.alibaba.fastjson.support.spring;

    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.parser.Feature;
    import com.alibaba.fastjson.serializer.SerializerFeature;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.nio.charset.Charset;
    import org.springframework.http.HttpInputMessage;
    import org.springframework.http.HttpOutputMessage;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.AbstractHttpMessageConverter;
    import org.springframework.http.converter.HttpMessageNotReadableException;
    import org.springframework.http.converter.HttpMessageNotWritableException;

    public class FastJsonHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
      public static final Charset UTF8 = Charset.forName("UTF-8");
      private Charset charset;
      private SerializerFeature[] features;

      public FastJsonHttpMessageConverter() {
          super(new MediaType[]{new MediaType("application", "json", UTF8), new MediaType("application", "*+json", UTF8)});
          this.charset = UTF8;
          this.features = new SerializerFeature[0];
      }

      protected boolean supports(Class<?> clazz) {
          return true;
      }

      public Charset getCharset() {
          return this.charset;
      }

      public void setCharset(Charset charset) {
          this.charset = charset;
      }

      public SerializerFeature[] getFeatures() {
          return this.features;
      }

      public void setFeatures(SerializerFeature... features) {
          this.features = features;
      }

      protected Object readInternal(Class<? extends Object> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          InputStream in = inputMessage.getBody();
          byte[] buf = new byte[1024];

          while(true) {
              int len = in.read(buf);
              if(len == -1) {
                  byte[] bytes = baos.toByteArray();
                  return JSON.parseObject(bytes, 0, bytes.length, this.charset.newDecoder(), clazz, new Feature[0]);
              }

              if(len > 0) {
                  baos.write(buf, 0, len);
              }
          }
      }

      protected void writeInternal(Object obj, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
          OutputStream out = outputMessage.getBody();
          String text = JSON.toJSONString(obj, this.features);
          byte[] bytes = text.getBytes(this.charset);
          out.write(bytes);
      }

以下是在解决这个问题时搜到的比较有用的帖子

Jackson has been the default json library in springframework until version 4.1 where it added support to use Gson by configuring GsonHttpMessageConverter. Let's take a look at how to configure your spring application to use Google Gson library's Gson class.

Detailed Video Notes

Gson is a java based library that converts java objects into their JSON representation and vice versa. A common example is when you are create a REST end point via a spring @Controller where you fetch a list of objects that you want to convert into an jsonarray. Let's examine a few different configuration options within your spring boot, java config and xml based applications.

Getting started

[0:21]

Before we get started it is important to understand how spring converts objects to json. In typical spring mvc once request exits the @Controller it looks for a view to render. When specifying a @RequestBody or a @RestController we ask spring to by pass this step and write the java objects in the model directly to the response. When doing so spring will look specifically for a HttpMessageConverter associated to the mime type to perform the conversion and in our case Json. Spring configures MappingJackson2HttpMessageConverter by default so we want to swap it with GsonHttpMessageConverter so it uses Gson to convert the java objects.

For our first snippet, lets generate a spring boot application from spring initializr web page and import it into eclipse.

Configuring gson in spring boot

[1:1]

Adding gson to classpath

If you are setting up Gson to work with spring boot you will first want to look at HttpMessageConvertersAutoConfiguration as there may be configuration changes. As it exists during the write up of this tutorial the GsonHttpMessageConverter will be created if Gson is on your classpath, the application doesn't contain jackson's JsonGenerator class and if the Gson bean doesn't exist already.

@Configuration
@ConditionalOnClass(Gson.class)
@ConditionalOnMissingClass(name = "com.fasterxml.jackson.core.JsonGenerator")
@ConditionalOnBean(Gson.class)
protected static class GsonHttpMessageConverterConfiguration {

  @Bean
  @ConditionalOnMissingBean
  public GsonHttpMessageConverter gsonHttpMessageConverter(Gson gson) {
      GsonHttpMessageConverter converter = new GsonHttpMessageConverter();
      converter.setGson(gson);
      return converter;
  }

}
<dependency>
  <groupId>com.google.code.gson</groupId>
  <artifactId>gson</artifactId>
  <version>2.3.1</version>
</dependency>

Excluding jackson from classpath

[1:29]

If you want to be sure that jackson isn't used or if you are experiencing conflicts you can add @EnableAutoConfiguration(exclude = { JacksonAutoConfiguration.class }) to your application class and exclude it from the spring-boot-starter-web dependency.

@SpringBootApplication
@EnableAutoConfiguration(exclude = { JacksonAutoConfiguration.class })
public class Application {

  public static void main(String[] args) {
      SpringApplication.run(Application.class, args);
  }
}
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
      <exclusions>
      <exclusion>
          <artifactId>jackson-databind</artifactId>
          <groupId>com.fasterxml.jackson.core</groupId>
      </exclusion>
  </exclusions>
</dependency>

If you are trying to eliminate the dependency on jackson we did notice as we worked through this tutorial that it was needed if you are using spring boot actuator specifically in EndpointAutoConfiguration class and it is a reported github issue.

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'configurationPropertiesReportEndpoint' defined in class path resource [org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.endpoint.ConfigurationPropertiesReportEndpoint]: Factory method 'configurationPropertiesReportEndpoint' threw exception; nested exception is java.lang.NoClassDefFoundError: com/fasterxml/jackson/databind/ser/BeanSerializerModifier

Validate request

Let's create a controller that returns a hashmap to validate that GsonHttpMessageConverter is being used. We will modify our application.properties to include logging.level.org.springframework=DEBUG and make a request to /default request mapping. Inspecting the log we can see that the GsonHttpMessageConverter is being used.

@RestController
public class MyController {

  @RequestMapping(value = "/",
          produces = { MediaType.APPLICATION_JSON_VALUE },
          method = RequestMethod.GET)
  public ResponseEntity<Map<String, String>> getContacts() {

      Map<String, String> dummyData = new HashMap<>();

      dummyData.put("java-examples",
              "http://www.leveluplunch.com/java/examples/");
      dummyData.put("groovy-examples",
              "http://www.leveluplunch.com/groovy/examples/");

      return new ResponseEntity<Map<String, String>>(dummyData, HttpStatus.OK);
  }
}
2015-01-18 07:33:51.673 DEBUG 18971 --- [nio-8080-exec-6] o.s.w.s.m.m.a.HttpEntityMethodProcessor  : Written [{java-examples=http://www.leveluplunch.com/java/examples/, groovy-examples=http://www.leveluplunch.com/groovy/examples/}] as "application/json" using [org.springframework.http.converter.json.GsonHttpMessageConverter@469047b8]

Customize converters using HttpMessageConverters

[2:14]

If you are having troubles with configuration just discussed or interested in customizing an existing converter by overriding a bean, spring-boot provides an alternative configuration option. This method will also allow you to have both gson and jackson on your class path. We will create a CustomConfiguration class but this could be performed in any class that contains the @Configuration annotation and create a new bean of type HttpMessageConverters. In our configuration we will instruct springframework to use the default HttpMessageConverter and then append GsonHttpMessageConverter.

@Configuration
public class CustomConfiguration {

  @Bean
  public HttpMessageConverters customConverters() {

      Collection<HttpMessageConverter<?>> messageConverters = new ArrayList<>();

      GsonHttpMessageConverter gsonHttpMessageConverter = new GsonHttpMessageConverter();
      messageConverters.add(gsonHttpMessageConverter);

      return new HttpMessageConverters(true, messageConverters);
  }
}

If we include the gson pom dependency and specify the customConverters it will configure GsonHttpMessageConverter to be used. Now you might be asking, if the jackson is also included on the classpath, how does this work? The default behavior of HttpMessageConverters when adding a new converter is to add it to the front of the list so since jackson is configured before it works.

Using java config

[3:7]

If you haven't moved to spring boot yet you still configure gson within your application using javaconfig by extending WebMvcConfigurerAdapter or if you need more control use WebMvcConfigurationSupport. You can read more on on how to customize the provided spring mvc configuration.

@Configuration
@EnableWebMvc
public class Application extends WebMvcConfigurerAdapter {

  @Override
  public void configureMessageConverters(List<HttpMessageConverter < ? >> converters) {
      GsonHttpMessageConverter gsonHttpMessageConverter = new GsonHttpMessageConverter();
      converters.add(gsonHttpMessageConverter);
  }
}

Configure Gson using XML configuration

[3:26]

I didn't add a specific test scenario for configuring gson in a xml based application but it would look something similar to the following:

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
  <property name="messageConverters">
      <list>
          <bean class="org.springframework.http.converter.json.GsonHttpMessageConverter"/>
      </list>
  </property>
</bean>

There has been a lot of debates on which java library is the fastest JSON processor and many of them might loose out to java 8 json api once support broadens. So until then pick a method and library above.

Thanks for joining in today's level up, have a great day!

Link:http://blog.csdn.net/do_bset_yourself/article/details/51324186

Link:https://www.leveluplunch.com/java/tutorials/023-configure-integrate-gson-spring-boot/

Spring-MVC配置Gson做为Message Converter解析Json的更多相关文章

  1. Tomcat配置和Spring MVC配置

    Tomcat启动时,先找系统变量CATALINA_BASE,如果没有,则找CATALINA_HOME.然后找这个变量所指的目录下的conf文件夹,从中读取配置文件.最重要的配置文件:server.xm ...

  2. MQTT 4 ——MQTT的Spring Mvc 配置接收字节流数据

    本篇记录一下MQTT整合Spring Mvc配置直接收发字节流数据 设备方是纯C开发,并且为了交互数据的安全,将传送的数据用了AES CBC进行了加密. 接下来正常方便做法应该是 将加密后的字节流转换 ...

  3. spring MVC配置详解

    现在主流的Web MVC框架除了Struts这个主力 外,其次就是Spring MVC了,因此这也是作为一名程序员需要掌握的主流框架,框架选择多了,应对多变的需求和业务时,可实行的方案自然就多了.不过 ...

  4. Spring mvc 配置详解

    现在主流的Web MVC框架除了Struts这个主力 外,其次就是Spring MVC了,因此这也是作为一名程序员需要掌握的主流框架,框架选择多了,应对多变的需求和业务时,可实行的方案自然就多了.不过 ...

  5. spring MVC配置详解(转)

    现在主流的Web MVC框架除了Struts这个主力 外,其次就是Spring MVC了,因此这也是作为一名程序员需要掌握的主流框架,框架选择多了,应对多变的需求和业务时,可实行的方案自然就多了.不过 ...

  6. Spring MVC配置静态资源和资源包

    Spring MVC配置静态资源和资源包 本例映射:css目录: pom.xml <properties> <spring.version>4.3.5.RELEASE</ ...

  7. 最小可用 Spring MVC 配置

    [最小可用 Spring MVC 配置] 1.导入有概率用到的JAR包, -> pom.xml 的更佳实践 - 1.0 <- <project xmlns="http:// ...

  8. Spring MVC配置详解(3)

    一.Spring MVC环境搭建:(Spring 2.5.6 + Hibernate 3.2.0) 1. jar包引入 Spring 2.5.6:spring.jar.spring-webmvc.ja ...

  9. Spring MVC 配置类 WebMvcConfigurerAdapter

    WebMvcConfigurerAdapter配置类是spring提供的一种配置方式,采用JavaBean的方式替代传统的基于xml的配置来对spring框架进行自定义的配置.因此,在spring b ...

随机推荐

  1. Excel 2007 若干技巧。

    1.自定义序列 office按钮→excel选项→常用→编辑自定义列表 2.无法清空剪贴板错误的处理办法: 取消"显示粘贴选项"选项 3.每次选定同一单元格 输入后按ctrl+En ...

  2. How to check WWN and Multipathing on Windows Server

    There are many ways to find the World Wide Name (WWN) of fibre channel HBA connected to windows serv ...

  3. 禁用系统的Ctrl+Alt+Left/Right(方向键)

    对于非常多工具,如IntelliJ IDE,Ctrl+Alt+Left/Right(方向键)是一个非常重要的快捷键,可是这个快捷键经常会被一些显示相关的附属应用给占用用于调整屏幕显示的朝向,有时候即使 ...

  4. umeng友盟消息推送功能集成

    umeng友盟消息推送功能集成(本人使用的是eclipse开发) 1.首先请自行观看友盟消息推送集成的API文档. 观看地址如下: http://dev.umeng.com/push/android/ ...

  5. 【DB2】性能管理视图

    1.性能管理部分视图列表 可以使用命令db2 list tables for schema sysibmadm获取所有的性能管理视图 视图名称              模式名            ...

  6. SpringBoot之actuator

    在springBoot中集成actuator可以很方便的管理和监控应用的状态. 暴露的Restful接口有: HTTP方法 路径 描述 鉴权 GET /autoconfig 查看自动配置的使用情况 t ...

  7. 将excel表导入到mysql中

    //导入excel表 方法一: )打开Excel另存为CSV文件 )将文件编码转化为utf8,用NotePad++打开csv文件,选择格式—转为utf8编码格式—保存 )在MySQL建表,字段的顺序要 ...

  8. 读书笔记——spring cloud 中 HystrixCommand的四种执行方式简述

    读了<Spring Cloud 微服务实战>第151-154页, 总结如下: Hystrix存在两种Command,一种是HystrixCommand,另一种是HystrixObserva ...

  9. Android 控件进阶修炼-仿360手机卫士波浪球进度控件

    技术:Android+java   概述 像360卫士的波浪球进度的效果,一般最常用的方法就是 画线的方式,先绘sin线或贝塞尔曲线,然后从左到右绘制竖线,然后再裁剪圆区域. 今天我这用图片bitma ...

  10. OpenGL实现通用GPU计算概述

    可能比較早一点做GPU计算的开发者会对OpenGL做通用GPU计算,随着GPU计算技术的兴起,越来越多的技术出现,比方OpenCL.CUDA.OpenAcc等,这些都是专门用来做并行计算的标准或者说接 ...