升级到新的spring版本后,Spring MockMvc UnsupportedOperationException

时间:2015-08-19 17:06:39

标签: java spring spring-mvc jackson spring-test

我正在从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的其他一些实现。 任何想法问题在哪里?

1 个答案:

答案 0 :(得分:3)

这是Spring Framework 4.1.3中引入的一个重大变化。

我创建了一个错误报告,以确保解决此问题:

https://jira.spring.io/browse/SPR-13375

感谢您注意这一点!

顺便说一句,当您使用MockMvcBuilders.standaloneSetup()时,没有需要通过ApplicationContext@ContextConfiguration加载@WebAppConfiguration。如果您分析测试类,您会发现实际上甚至没有使用WebApplicationContext进入测试的@Autowired。因此,您可以安全地删除所有配置。

请注意,由于您使用TestConfig直接测试控制器实例,MockMvcMockMvcBuilders.standaloneSetup() <{1}}。换句话说,您的自定义SexTypeDeserializer未在如此配置的测试中使用。如果您想继续使用独立设置,可以使用自定义配置MappingJackson2HttpMessageConverter(以及自定义序列化程序等)配置ObjectMapper并将其传递给StandaloneMockMvcBuilder通过其setMessageConverters(...)方法。

作为替代方案,您可以考虑使用MockMvcBuilders.webAppContextSetup(context)以在测试中实际使用SexTypeDeserializer以及其他生产的Spring MVC配置。