我不喜欢杰克逊。
我想使用ajax但使用Google Gson。
所以我试图弄清楚如何实现我自己的HttpMessageConverter与@ResponseBody注释一起使用它。 有人可以花点时间告诉我应该去的方式吗?我应该打开哪些配置? 另外我想知道我是否可以这样做并仍然使用< mvc:annotation-driven />?
提前致谢。
我已经在Spring社区Foruns问过它大约3天前没有回答所以我在这里问我是否有更好的机会。 Spring Community Forums link to my question
我也在网上进行了详尽的搜索,发现了一些关于这个主题的有趣内容,但似乎他们正考虑将它放在Spring 3.1中,我仍然使用spring 3.0.5: Jira's Spring Improvement ask
嗯......现在我正在尝试调试Spring代码以找出自己如何做到这一点,但我遇到了一些问题,比如我在这里说过: Spring Framework Build Error
如果有其他方法可以做到这一点,我想错过它,请告诉我。
答案 0 :(得分:37)
嗯......很难找到答案,我不得不关注那些不完整信息的线索,我认为在这里发布完整答案会更好。所以下一个搜索它会更容易。
首先,我必须实现自定义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>
请注意,要使 Formater 和 Validator 生效,我们还必须构建自定义 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 能够处理格式和验证,我们必须配置验证器, conversionService 并构建自定义< EM>的WebBindingInitializer
我希望除了我之外,所有这些都能帮到别人。
在我绝望的搜索中,this @Bozho帖子非常有用。我也很感谢@GaryF,他的回答把我带到@Bozho post。 对于那些试图在Spring 3.1中执行此操作的人,请参阅@Robby Pond回答..更容易,不是吗?
答案 1 :(得分:16)
您需要创建一个扩展AbstractHttpMessageConverter的GsonMessageConverter,并使用m vc-message-converters标记注册您的邮件转换器。该标签将使您的转换器优先于杰克逊转换器。
答案 2 :(得分:5)
我有一种情况,杰克逊的使用要求我改变其他组(在同一家公司)的代码。不喜欢那样。所以我选择使用Gson并根据需要注册TypeAdapters。
使用spring-test(曾经是spring-mvc-test)编写了一些转换器并编写了一些集成测试。无论我尝试了什么变化(使用mvc:annotation-driven OR手动定义bean)。他们都没有工作。这些的任何组合总是使用杰克逊转换器继续失败。
<强>答案强>&GT;事实证明,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();
.
.
}
希望这有助于某人,最后我使用注释驱动和重新设计android的转换器
答案 3 :(得分:5)
如果你想在不弄乱xml的情况下添加消息转换器,这是一个简单的例子
@Autowired
private RequestMappingHandlerAdapter adapter;
@PostConstruct
public void initStuff() {
List<HttpMessageConverter<?>> messageConverters = adapter.getMessageConverters();
BufferedImageHttpMessageConverter imageConverter = new BufferedImageHttpMessageConverter();;
messageConverters.add(0,imageConverter);
}
答案 4 :(得分:3)
Robby Pond基本上是正确的,但请注意他建议使用mvc:message-converters标签要求你使用3.1。由于3.1目前只是一个里程碑版本(M1),我建议在创建后以这种方式注册转换器:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<util:list id="beanList">
<ref bean="someMessageConverter"/>
<ref bean="someOtherMessageConverter"/>
</util:list>
</property>
</bean>
答案 5 :(得分:3)
或者如Jira's Spring Improvement ask中所述,编写一个将HttpMessageConvertor
添加到AnnotationMethodHandlerAdapter
答案 6 :(得分:3)
请注意,GsonHttpMessageConverter最近添加到Spring(4.1)
答案 7 :(得分:0)
您可以通过将WebConfig文件编写为Java文件来完成此操作。使用WebMvcConfigurerAdapter扩展配置文件并覆盖extendMessageConverters方法以添加您想要的消息转换器。此方法将保留Spring添加的默认转换器,并在最后添加转换器。显然,您可以完全控制列表,并且可以在列表中添加所需的位置。
@Configuration
@EnableWebMvc
@ComponentScan(basePackageClasses={WebConfig.class})
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new GsonHttpMessageConverter());
}
}
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 "";
}
}