使用@ResponseBody定制HttpMessageConverter来做Json的事情

Iog*_*gui 31 spring json spring-mvc gson

我不喜欢杰克逊.

我想使用ajax但是使用Google Gson.

所以我试图弄清楚如何实现我自己的HttpMessageConverter与@ResponseBody注释一起使用它.有人可以花点时间告诉我应该去的方式吗?我应该打开哪些配置?另外我想知道我是否可以这样做并仍然使用<mvc:annotation-driven />?

提前致谢.

大约3天前我已经在Spring Community Foruns中问过它没有回答所以我在这里问我是否有更好的机会. Spring社区论坛链接到我的问题

我也在网上进行了详尽的搜索,发现了一些关于这个主题的有趣内容,但似乎他们正考虑将它放在Spring 3.1中,我仍然使用spring 3.0.5: Jira的Spring Improvement ask

嗯...现在我正在尝试调试Spring代码以找出自己如何做到这一点,但我遇到了一些问题,比如我在这里说过: Spring Framework Build Error

如果有另一种方法可以做到这一点,我想念它,请告诉我.

Iog*_*gui 38

嗯......很难找到答案,我不得不关注那些不完整信息的线索,我认为在这里发布完整答案会更好.所以下一个搜索它会更容易.

首先,我必须实现自定义HttpMessageConverter:


package net.iogui.web.spring.converter;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
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;

import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;

public class GsonHttpMessageConverter extends AbstractHttpMessageConverter<Object> {

    private Gson gson = new Gson();

    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

    public GsonHttpMessageConverter(){
        super(new MediaType("application", "json", DEFAULT_CHARSET));
    }

    @Override
    protected Object readInternal(Class<? extends Object> clazz,
                                  HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {

        try{
            return gson.fromJson(convertStreamToString(inputMessage.getBody()), clazz);
        }catch(JsonSyntaxException e){
            throw new HttpMessageNotReadableException("Could not read JSON: " + e.getMessage(), e);
        }

    }

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

    @Override
    protected void writeInternal(Object t, 
                                 HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {

        //TODO: adapt this to be able to receive a list of json objects too

        String json = gson.toJson(t);

        outputMessage.getBody().write(json.getBytes());
    }

    //TODO: move this to a more appropriated utils class
    public String convertStreamToString(InputStream is) throws IOException {
        /*
         * To convert the InputStream to String we use the Reader.read(char[]
         * buffer) method. We iterate until the Reader return -1 which means
         * there's no more data to read. We use the StringWriter class to
         * produce the string.
         */
        if (is != null) {
            Writer writer = new StringWriter();

            char[] buffer = new char[1024];
            try {
                Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
                int n;
                while ((n = reader.read(buffer)) != -1) {
                    writer.write(buffer, 0, n);
                }
            } finally {
                is.close();
            }
            return writer.toString();
        } else {
            return "";
        }
    }

}

然后我不得不剥离annnotaion驱动的标签,并自己配置spring-mvc配置文件:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <!-- Configures the @Controller programming model -->

    <!-- To use just with a JSR-303 provider in the classpath 
    <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
    -->

    <bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean" />

    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="webBindingInitializer">
            <bean class="net.iogui.web.spring.util.CommonWebBindingInitializer" />
        </property>
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter" />
                <bean class="org.springframework.http.converter.StringHttpMessageConverter" />
                <bean class="org.springframework.http.converter.ResourceHttpMessageConverter" />
                <bean class="net.iogui.web.spring.converter.GsonHttpMessageConverter" />
                <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter" />
                <bean class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter" />
                <!-- bean class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter" /-->
            </list>
        </property>
    </bean>
    <bean id="handlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />


    <context:component-scan base-package="net.iogui.teste.web.controller"/>

    <!-- Forwards requests to the "/" resource to the "login" view -->
    <mvc:view-controller path="/" view-name="home"/>

    <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources/ directory -->
    <mvc:resources mapping="/resources/**" location="/resources/" />

    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/view/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

看到这一点,为了使FormaterValidator工作,我们还必须构建一个自定义的webBindingInitializer:


package net.iogui.web.spring.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.validation.Validator;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.support.WebBindingInitializer;
import org.springframework.web.context.request.WebRequest;

public class CommonWebBindingInitializer implements WebBindingInitializer {

    @Autowired(required=false)
    private Validator validator;

    @Autowired
    private ConversionService conversionService;

    @Override
    public void initBinder(WebDataBinder binder, WebRequest request) {
        binder.setValidator(validator);
        binder.setConversionService(conversionService);
    }

}

看到一个有趣的事情是,为了使配置工作没有annotaion驱动的标签,我们必须手动配置AnnotationMethodHandlerAdapter上DefaultAnnotationHandlerMapping.为了使AnnotationMethodHandlerAdapter能够处理格式化和验证,我们必须配置验证器,转换服务并构建自定义webBindingInitializer.

我希望这一切能帮助除了我之外的其他人.

在我绝望的搜索中,这个 @Bozho帖子非常有用.我也很感谢@GaryF,他的回答把我带到了@Bozho的帖子.对于那些试图在Spring 3.1中执行此操作的人,请参阅@Robby Pond回答..更容易,不是吗?


Rob*_*ond 16

您需要创建一个扩展AbstractHttpMessageConverter的GsonMessageConverter,并使用m vc-message-converters标记来注册您的消息转换器.该标签将使您的转换器优先于Jackson转换器.


小智 5

我有情况,杰克逊的使用将要求我改变其他组(在同一公司)的代码.不喜欢那样.所以我选择使用Gson并根据需要注册TypeAdapters.

连接转换器并使用spring-test(曾经是spring-mvc-test)编写了一些集成测试.无论我尝试了什么变化(使用mvc:annotation-driven OR手动定义bean).他们都没有工作.这些的任何组合总是使用继续失败的杰克逊转换器.

答案 >事实证明,MockMvcBuilders的standaloneSetup方法"硬"将消息转换器编码为默认版本,并忽略了我上面的所有更改.这是有效的:

@Autowired
private RequestMappingHandlerAdapter adapter;

public void someOperation() {
  StandaloneMockMvcBuilder smmb = MockMvcBuilders.standaloneSetup(controllerToTest);
  List<HttpMessageConverter<?>> converters = adapter.getMessageConverters();
  HttpMessageConverter<?> ary[] = new HttpMessageConverter[converters.size()];
  smmb.setMessageConverters(conveters.toArray(ary));
  mockMvc = smmb.build();
   .
   .
}
Run Code Online (Sandbox Code Playgroud)

希望这有助于某人,最后我使用注释驱动和重新设计android的转换器


Eri*_*ino 5

如果你想添加一个消息转换器而不弄乱xml,这是一个简单的例子

@Autowired
private RequestMappingHandlerAdapter adapter;

@PostConstruct
public void initStuff() {
    List<HttpMessageConverter<?>> messageConverters = adapter.getMessageConverters();
    BufferedImageHttpMessageConverter imageConverter = new BufferedImageHttpMessageConverter();;
    messageConverters.add(0,imageConverter);
}
Run Code Online (Sandbox Code Playgroud)