使用@ResponseBody 自定义 HttpMessageConverter 来做 Json 的事情 [英] Custom HttpMessageConverter with @ResponseBody to do Json things

查看:37
本文介绍了使用@ResponseBody 自定义 HttpMessageConverter 来做 Json 的事情的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不喜欢杰克逊.

我想使用 ajax,但要使用 Google Gson.

所以我想弄清楚如何实现我自己的 HttpMessageConverter 以将它与 @ResponseBody 注释一起使用.有人可以花点时间向我展示我应该走的路吗?我应该开启哪些配置?另外我想知道我是否可以做到这一点并且仍然使用 <mvc:annotation-driven/>?

提前致谢.

大约 3 天前我已经在 Spring Community Foruns 上问过这个问题,但没有得到答复,所以我在这里问,看看我是否有更好的机会.Spring 社区论坛链接到我的问题

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

嗯...现在我正在尝试调试 Spring 代码以找出自己如何执行此操作,但是我遇到了一些问题,就像我在这里所说的那样:Spring 框架构建错误

如果有其他方法可以做到这一点,但我错过了,请告诉我.

解决方案

嗯……找到答案太难了,我不得不跟踪很多不完整信息的线索,我认为发布完整的答案在这里.所以下一个搜索这个会更容易.

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

<前><代码>包 net.iogui.web.spring.converter;导入 java.io.BufferedReader;导入 java.io.IOException;导入 java.io.InputStream;导入 java.io.InputStreamReader;导入 java.io.Reader;导入 java.io.StringWriter;导入 java.io.Writer;导入 java.nio.charset.Charset;导入 org.springframework.http.HttpInputMessage;导入 org.springframework.http.HttpOutputMessage;导入 org.springframework.http.MediaType;导入 org.springframework.http.converter.AbstractHttpMessageConverter;导入 org.springframework.http.converter.HttpMessageNotReadableException;导入 org.springframework.http.converter.HttpMessageNotWritableException;导入 com.google.gson.Gson;导入 com.google.gson.JsonSyntaxException;公共类 GsonHttpMessageConverter 扩展 AbstractHttpMessageConverter<Object>{私人 Gson gson = new Gson();public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");公共 GsonHttpMessageConverter(){super(new MediaType("application", "json", DEFAULT_CHARSET));}@覆盖protected Object readInternal(Class clazz,HttpInputMessage inputMessage) 抛出 IOException, HttpMessageNotReadableException {尝试{返回 gson.fromJson(convertStreamToString(inputMessage.getBody()), clazz);}catch(JsonSyntaxException e){throw new HttpMessageNotReadableException("无法读取 JSON:" + e.getMessage(), e);}}@覆盖受保护的布尔支持(类 clazz){返回真;}@覆盖protected void writeInternal(Object t,HttpOutputMessage outputMessage) 抛出 IOException, HttpMessageNotWritableException {//TODO: 调整它也能够接收 json 对象列表字符串 json = gson.toJson(t);outputMessage.getBody().write(json.getBytes());}//TODO: 将其移至更合适的 utils 类公共字符串 convertStreamToString(InputStream is) 抛出 IOException {/** 要将 InputStream 转换为 String 我们使用 Reader.read(char[]* 缓冲)方法.我们迭代直到 Reader 返回 -1,这意味着* 没有更多的数据要读取.我们使用 StringWriter 类来* 产生字符串.*/如果(是!= null){Writer writer = new StringWriter();字符 [] 缓冲区 = 新字符 [1024];尝试 {Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));国际n;while ((n = reader.read(buffer)) != -1) {writer.write(buffer, 0, n);}} 最后 {is.close();}返回 writer.toString();} 别的 {返回 "";}}}

然后我不得不去掉 annotaion-driven 标签,并在 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.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"><!-- 配置@Controller编程模型--><!-- 仅与类路径中的 JSR-303 提供程序一起使用<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"><属性名称=webBindingInitializer"><bean class="net.iogui.web.spring.util.CommonWebBindingInitializer"/></属性><属性名称="messageConverters"><列表><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></属性></bean><bean id="handlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/><context:component-scan base-package="net.iogui.teste.web.controller"/><!-- 将/"资源的请求转发到登录"视图--><mvc:view-controller path="/" view-name="home"/><!-- 通过有效地提供 ${webappRoot}/resources/目录中的静态资源来处理/resources/** 的 HTTP GET 请求 --><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></豆类>

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

<前><代码>包 net.iogui.web.spring.util;导入 org.springframework.beans.factory.annotation.Autowired;导入 org.springframework.core.convert.ConversionService;导入 org.springframework.validation.Validator;导入 org.springframework.web.bind.WebDataBinder;导入 org.springframework.web.bind.support.WebBindingInitializer;导入 org.springframework.web.context.request.WebRequest;公共类 CommonWebBindingInitializer 实现 WebBindingInitializer {@Autowired(必需=假)私有验证器验证器;@自动连线私有 ConversionService 转换服务;@覆盖公共无效initBinder(WebDataBinder绑定器,WebRequest请求){binder.setValidator(验证器);binder.setConversionService(conversionService);}}

一个有趣的事情是,为了使配置在没有 annotation-driven 标签的情况下工作,我们必须手动配置一个 AnnotationMethodHandlerAdapter 和一个 DefaultAnnotationHandlerMapping.为了使 AnnotationMethodHandlerAdapter 能够处理格式化和验证,我们必须配置一个 validator、一个 conversionService 并构建一个自定义 webBindingInitializer.

我希望这对我以外的其他人有帮助.

在我绝望的搜索中,这篇@Bozho 帖子非常实用.我也很感谢@GaryF 因为他的回答把我带到了 @Bozho 帖子.对于尝试在 Spring 3.1 中执行此操作的您,请参阅 @Robby Pond 的答案.. 容易多了,不是吗?

I don't like Jackson.

I want to use ajax but with Google Gson.

So I'm trying to figure out how to implement my own HttpMessageConverter to use it with @ResponseBody annotation. Can someone take a time to show me the way I should go? What configurations should I turn on? Also I'm wondering if I can do this and still use <mvc:annotation-driven />?

Thanks in advance.

I've already asked it in Spring Community Foruns about 3 days ago with no answer so I'm asking here to see if I get a better chance. Spring Community Forums link to my question

I've also made an exhaustive search on the web and found something interesting on this subject but it seems they're thinking to put it in Spring 3.1 and I'm still using spring 3.0.5: Jira's Spring Improvement ask

Well... now I'm trying to debug Spring code to find out myself how to do this, but I'm having some problems like I've said here: Spring Framework Build Error

If there is another way to do this and I'm missing it, please let me know.

解决方案

Well... it was so hard to find the answer and I had to follow so many clues to incomplete information that I think it will be good to post the complete answer here. So it will be easier for the next one searching for this.

First I had to implement the custom 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 "";
        }
    }

}

Then I had to strip off the annnotaion-driven tag and configure all by my own hands on the spring-mvc configuration file:


<?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>

See that, to make the Formater and Validator to work, we have to build a custom webBindingInitializer too:


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);
    }

}

An Interesting thing to see is that In order to make the configuration work without the annotaion-driven tag, we have to manually configure a AnnotationMethodHandlerAdapter and a DefaultAnnotationHandlerMapping. And in order to make the AnnotationMethodHandlerAdapter capable of handling formatting and validation, we had to configure a validator, a conversionService and to build a custom webBindingInitializer.

I hope all this helps someone else besides me.

On my desperate search, this @Bozho post was extremely util. I am also grateful to @GaryF couse his answer took me to the @Bozho post. To you that are trying to do this in Spring 3.1, see @Robby Pond answer.. A lot easier, isn't it?

这篇关于使用@ResponseBody 自定义 HttpMessageConverter 来做 Json 的事情的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆