Spring 升级到新 spring 版本后 MockMvc UnsupportedOperationException
Spring MockMvc UnsupportedOperationException after upgrading to new spring version
我正在从 Spring 3.2.3.RELEASE 迁移到 Spring 4.2.0.RELEASE。
我被卡住了,因为现有的测试开始失败。我的代码看起来像(简化):
@WebAppConfiguration
@ContextConfiguration(classes = [TestConfig.class])
class MvcTest extends Specification {
@Autowired
WebApplicationContext context
MockMvc mockMvc
@Subject
MyController controller
def setup() {
controller = new MyController()
mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
}
def "should not allow to save an invalid entity"() {
when:
def result = mockMvc.perform(post("/people")
.content('''{
"name": "",
"age": 21,
"sex": 1
}''')
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
then:
result.andDo(print())
.andExpect(status().isBadRequest())
.andExpect(jsonPath('$.message').value("Name cannot be empty."))
}
}
MyController.java
@Controller
public class MyController {
@RequestMapping(method = RequestMethod.POST,
value = "/people",
produces = "application/json",
consumes = "application/json")
@ResponseBody
public Response create(@RequestBody @Valid Person person) {
//save person to repository. Debugger is not even entering this line...
}
}
Person.java:
public class Person {
private int age;
@NotEmpty(message = "Name cannot be empty.")
private String name;
@NotNull
@JsonSerialize(using = SexTypeSerializer.class)
@JsonDeserialize(using = SexTypeDeserializer.class)
private Sex sex;
//getters, setters, other methods
}
TestConfig.groovy
@EnableWebMvc
@Configuration
class TestConfig {
@Bean
@Primary
public ObjectMapper objectMapper() {
def objectMapper = new ObjectMapper()
def jacksonModule = new SimpleModule()
jacksonModule.addDeserializer(Sex.class, new SexTypeDeserializer())
jacksonModule.addSerializer(Sex.class, new SexTypeSerializer())
objectMapper.registerModule(jacksonModule)
objectMapper
}
}
堆栈跟踪:
java.lang.UnsupportedOperationException
at org.springframework.test.web.servlet.setup.StubWebApplicationContext$StubBeanFactory.createBean(StubWebApplicationContext.java:369)
at org.springframework.http.converter.json.SpringHandlerInstantiator.deserializerInstance(SpringHandlerInstantiator.java:68)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.deserializerInstance(DefaultDeserializationContext.java:111)
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findDeserializerFromAnnotation(BasicDeserializerFactory.java:1436)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.constructSettableProperty(BeanDeserializerFactory.java:765)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.addBeanProps(BeanDeserializerFactory.java:544)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:270)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:168)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:401)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:350)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:263)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:243)
at com.fasterxml.jackson.databind.deser.DeserializerCache.hasValueDeserializerFor(DeserializerCache.java:193)
at com.fasterxml.jackson.databind.DeserializationContext.hasValueDeserializerFor(DeserializationContext.java:344)
at com.fasterxml.jackson.databind.ObjectMapper.canDeserialize(ObjectMapper.java:2035)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.canRead(AbstractJackson2HttpMessageConverter.java:151)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:187)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:148)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:125)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:78)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:129)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:111)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:806)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:729)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:688)
还提供了自定义序列化程序,并且可以与以前版本的 spring 一起正常工作。我认为配置有问题,因为调用了这样的方法:
StubWebApplicationContext.java
@Override
public <T> T createBean(Class<T> beanClass) {
throw new UnsupportedOperationException();
}
看起来应该使用一些其他的 createBean 实现。
有什么想法吗?
这是 Spring Framework 4.1.3 中引入的重大更改。
我已经创建了一个错误报告来确保这个问题得到解决:
https://jira.spring.io/browse/SPR-13375
感谢您提醒我们注意此事!
顺便说一句,当你使用MockMvcBuilders.standaloneSetup()
时,不需要通过@ContextConfiguration
和[=13加载一个ApplicationContext
=].如果你分析你的测试 class,你会发现你实际上什至没有使用你有 @Autowired
的 WebApplicationContext
到你的测试中。因此,您可以安全地删除所有该配置。
还请注意,您的 TestConfig
未在 MockMvc
中使用 ,因为您正在使用 MockMvcBuilders.standaloneSetup()
来测试控制器的实例直接地。换句话说,您的自定义 SexTypeDeserializer
未在这样配置的测试中使用。如果您想继续使用独立设置,您可以使用自定义配置的 ObjectMapper
(连同您的自定义序列化程序等)配置 MappingJackson2HttpMessageConverter
,并将其传递给 StandaloneMockMvcBuilder
通过其 setMessageConverters(...)
方法。
作为替代方案,您可以考虑使用 MockMvcBuilders.webAppContextSetup(context)
以便在您的测试中实际使用您的 SexTypeDeserializer
以及其余的生产 Spring MVC 配置。
我正在从 Spring 3.2.3.RELEASE 迁移到 Spring 4.2.0.RELEASE。 我被卡住了,因为现有的测试开始失败。我的代码看起来像(简化):
@WebAppConfiguration
@ContextConfiguration(classes = [TestConfig.class])
class MvcTest extends Specification {
@Autowired
WebApplicationContext context
MockMvc mockMvc
@Subject
MyController controller
def setup() {
controller = new MyController()
mockMvc = MockMvcBuilders.standaloneSetup(controller).build()
}
def "should not allow to save an invalid entity"() {
when:
def result = mockMvc.perform(post("/people")
.content('''{
"name": "",
"age": 21,
"sex": 1
}''')
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON))
then:
result.andDo(print())
.andExpect(status().isBadRequest())
.andExpect(jsonPath('$.message').value("Name cannot be empty."))
}
}
MyController.java
@Controller
public class MyController {
@RequestMapping(method = RequestMethod.POST,
value = "/people",
produces = "application/json",
consumes = "application/json")
@ResponseBody
public Response create(@RequestBody @Valid Person person) {
//save person to repository. Debugger is not even entering this line...
}
}
Person.java:
public class Person {
private int age;
@NotEmpty(message = "Name cannot be empty.")
private String name;
@NotNull
@JsonSerialize(using = SexTypeSerializer.class)
@JsonDeserialize(using = SexTypeDeserializer.class)
private Sex sex;
//getters, setters, other methods
}
TestConfig.groovy
@EnableWebMvc
@Configuration
class TestConfig {
@Bean
@Primary
public ObjectMapper objectMapper() {
def objectMapper = new ObjectMapper()
def jacksonModule = new SimpleModule()
jacksonModule.addDeserializer(Sex.class, new SexTypeDeserializer())
jacksonModule.addSerializer(Sex.class, new SexTypeSerializer())
objectMapper.registerModule(jacksonModule)
objectMapper
}
}
堆栈跟踪:
java.lang.UnsupportedOperationException
at org.springframework.test.web.servlet.setup.StubWebApplicationContext$StubBeanFactory.createBean(StubWebApplicationContext.java:369)
at org.springframework.http.converter.json.SpringHandlerInstantiator.deserializerInstance(SpringHandlerInstantiator.java:68)
at com.fasterxml.jackson.databind.deser.DefaultDeserializationContext.deserializerInstance(DefaultDeserializationContext.java:111)
at com.fasterxml.jackson.databind.deser.BasicDeserializerFactory.findDeserializerFromAnnotation(BasicDeserializerFactory.java:1436)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.constructSettableProperty(BeanDeserializerFactory.java:765)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.addBeanProps(BeanDeserializerFactory.java:544)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:270)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:168)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:401)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:350)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:263)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:243)
at com.fasterxml.jackson.databind.deser.DeserializerCache.hasValueDeserializerFor(DeserializerCache.java:193)
at com.fasterxml.jackson.databind.DeserializationContext.hasValueDeserializerFor(DeserializationContext.java:344)
at com.fasterxml.jackson.databind.ObjectMapper.canDeserialize(ObjectMapper.java:2035)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.canRead(AbstractJackson2HttpMessageConverter.java:151)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:187)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:148)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:125)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:78)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:129)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:111)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:806)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:729)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:688)
还提供了自定义序列化程序,并且可以与以前版本的 spring 一起正常工作。我认为配置有问题,因为调用了这样的方法:
StubWebApplicationContext.java
@Override
public <T> T createBean(Class<T> beanClass) {
throw new UnsupportedOperationException();
}
看起来应该使用一些其他的 createBean 实现。 有什么想法吗?
这是 Spring Framework 4.1.3 中引入的重大更改。
我已经创建了一个错误报告来确保这个问题得到解决:
https://jira.spring.io/browse/SPR-13375
感谢您提醒我们注意此事!
顺便说一句,当你使用MockMvcBuilders.standaloneSetup()
时,不需要通过@ContextConfiguration
和[=13加载一个ApplicationContext
=].如果你分析你的测试 class,你会发现你实际上什至没有使用你有 @Autowired
的 WebApplicationContext
到你的测试中。因此,您可以安全地删除所有该配置。
还请注意,您的 TestConfig
未在 MockMvc
中使用 ,因为您正在使用 MockMvcBuilders.standaloneSetup()
来测试控制器的实例直接地。换句话说,您的自定义 SexTypeDeserializer
未在这样配置的测试中使用。如果您想继续使用独立设置,您可以使用自定义配置的 ObjectMapper
(连同您的自定义序列化程序等)配置 MappingJackson2HttpMessageConverter
,并将其传递给 StandaloneMockMvcBuilder
通过其 setMessageConverters(...)
方法。
作为替代方案,您可以考虑使用 MockMvcBuilders.webAppContextSetup(context)
以便在您的测试中实际使用您的 SexTypeDeserializer
以及其余的生产 Spring MVC 配置。