我不确定这是否是我的错误配置,误解了通过@ModelAttribute
和自动JSON内容转换可以实现什么,或者Spring或Jackson中的错误。当然,如果结果是后者,我会向适当的人提出问题。
我在向控制器的处理程序方法添加@ModelAttribute
时遇到了问题。该方法的目的是公开从表单或先前提交中填充的bean,但我可以在不实际将数据提交到bean的情况下重现该问题。
我正在使用Spring mvc-showcase示例。它目前正在使用Spring 3.1,但我第一次遇到并且能够在我的3.0.5设置上重现这个问题。 mvc-showcase示例使用非常标准的servlet-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven conversion-service="conversionService">
<argument-resolvers>
<beans:bean class="org.springframework.samples.mvc.data.custom.CustomArgumentResolver"/>
</argument-resolvers>
</annotation-driven>
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources/ directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<!-- Imports user-defined @Controller beans that process client requests -->
<beans:import resource="controllers.xml" />
<!-- Only needed because we install custom converters to support the examples in the org.springframewok.samples.mvc.convert package -->
<beans:bean id="conversionService" class="org.springframework.samples.mvc.convert.CustomConversionServiceFactoryBean" />
<!-- Only needed because we require fileupload in the org.springframework.samples.mvc.fileupload package -->
<beans:bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
</beans:beans>
文件中引用的controllers.xml
只是为根路径设置相关的组件扫描和视图控制器。相关摘录如下。
<!-- Maps '/' requests to the 'home' view -->
<mvc:view-controller path="/" view-name="home"/>
<context:component-scan base-package="org.springframework.samples.mvc" />
我试图提供的测试bean是一个简单的POJO。
package org.springframework.samples.mvc.test;
public class TestBean {
private String testField = "test@example.com";
public String getTestField() {
return testField;
}
public void setTestField(String testField) {
this.testField = testField;
}
}
最后,控制器,这也很简单。
package org.springframework.samples.mvc.test;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("test/*")
public class TestController {
@ModelAttribute("testBean")
public TestBean getTestBean() {
return new TestBean();
}
@RequestMapping(value = "beanOnly", method = RequestMethod.POST)
public @ResponseBody
TestBean testBean(@ModelAttribute("testBean") TestBean bean) {
return bean;
}
@RequestMapping(value = "withoutModel", method = RequestMethod.POST)
public @ResponseBody
Model testWithoutModel(Model model) {
model.addAttribute("result", "success");
return model;
}
@RequestMapping(value = "withModel", method = RequestMethod.POST)
public @ResponseBody
Model testWithModel(Model model, @ModelAttribute("testBean") TestBean bean) {
bean.setTestField("This is the new value of testField");
model.addAttribute("result", "success");
return model;
}
}
如果我通过映射路径/mvc-showcase/test/beanOnly
调用控制器,我会按预期获得bean的JSON表示。调用withoutModel
处理程序提供与调用关联的Spring Model
对象的JSON表示。它包含来自返回值中的初始声明的隐式@ModelAttribute
,但该bean对该方法不可用。例如,如果我希望处理表单提交的结果并返回JSON响应消息,那么我需要该属性。
最后一个方法添加@ModelAttribute
,这就是问题出现的地方。调用/mvc-showcase/test/withModel
会导致异常。
在我的3.0.5安装中,由于缺少FormattingConversionService的序列化程序而导致JsonMappingException。在3.1.0示例中,异常是由缺少DefaultConversionService的序列化程序引起的。我将在这里包含3.1例外;它似乎有相同的根本原因,即使路径有点不同。
org.codehaus.jackson.map.JsonMappingException: No serializer found for class org.springframework.format.support.DefaultFormattingConversionService and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: org.springframework.validation.support.BindingAwareModelMap["org.springframework.validation.BindingResult.testBean"]->org.springframework.validation.BeanPropertyBindingResult["propertyAccessor"]->org.springframework.beans.BeanWrapperImpl["conversionService"])
at org.codehaus.jackson.map.ser.StdSerializerProvider$1.failForEmpty(StdSerializerProvider.java:89)
at org.codehaus.jackson.map.ser.StdSerializerProvider$1.serialize(StdSerializerProvider.java:62)
at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:272)
at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:147)
at org.codehaus.jackson.map.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:272)
at org.codehaus.jackson.map.ser.BeanSerializer.serializeFields(BeanSerializer.java:175)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:147)
at org.codehaus.jackson.map.ser.MapSerializer.serializeFields(MapSerializer.java:207)
at org.codehaus.jackson.map.ser.MapSerializer.serialize(MapSerializer.java:140)
at org.codehaus.jackson.map.ser.MapSerializer.serialize(MapSerializer.java:22)
at org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:315)
at org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:242)
at org.codehaus.jackson.map.ObjectMapper.writeValue(ObjectMapper.java:1030)
at org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.writeInternal(MappingJacksonHttpMessageConverter.java:153)
at org.springframework.http.converter.AbstractHttpMessageConverter.write(AbstractHttpMessageConverter.java:181)
at org.springframework.web.servlet.mvc.method.annotation.support.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:121)
at org.springframework.web.servlet.mvc.method.annotation.support.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:101)
at org.springframework.web.servlet.mvc.method.annotation.support.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:81)
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:64)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:114)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodAdapter.invokeHandlerMethod(RequestMappingHandlerMethodAdapter.java:505)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMethodAdapter.handleInternal(RequestMappingHandlerMethodAdapter.java:468)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:790)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:710)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:803)
at
...
那么,是否有一些我遗漏的配置应该允许Jackson转换器正确处理从方法签名中@ModelAttribute
的处理程序派生的响应?如果没有,任何想法是否更可能是一个Spring bug或Jackson bug?在这一点上,我倾向于春天。
答案 0 :(得分:1)
它看起来像一个Spring配置问题,当序列化为JSON时DefaultFormattingConversionService
为空,而Jackson(默认情况下)如果bean为空将抛出异常,请参阅features documentation中的FAIL_ON_EMPTY_BEANS
}。但我不清楚为什么豆子是空的。
如果您将FAIL_ON_EMPTY_BEANS
设置为false,应该有效,但仍然无法解释为什么它首先发生。
DefaultFormattingConversionService
是3.1的新功能 - 它扩展了FormattingConversionService,它解释了3.0.5和3.1之间的不同异常。
我不认为这是杰克逊的问题,虽然杰克逊(1.8.0)的新版本仅在3天前released,所以你也可以试试。
我会尝试在本地重现这个。