java - 升级到新的 Spring 版本后 Spring MockMvc UnsupportedOperationException

标签 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 的其他实现。 有什么想法问题出在哪里吗?

最佳答案

这是 Spring Framework 4.1.3 中引入的一项重大更改。

我创建了一个错误报告以确保该问题得到解决:

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

感谢您让我们注意到这一点!

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

另请注意,您的 TestConfigMockMvc使用,因为您使用的是 MockMvcBuilders.standaloneSetup() 直接测试 Controller 的实例。换句话说,您的自定义 SexTypeDeserializer 不会在这样配置的测试中使用。如果您想继续使用独立设置,您可以使用自定义配置的 ObjectMapper (以及自定义序列化器等)配置 MappingJackson2HttpMessageConverter 并将其传递通过其 setMessageConverters(...) 方法到 StandaloneMockMvcBuilder

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

关于java - 升级到新的 Spring 版本后 Spring MockMvc UnsupportedOperationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32101611/

相关文章:

android - Spring Android RestTemplate 可以返回列表吗?

java - Spring 数据JPA : how to return empty result when repository method parameter of Collection type is empty?

java - Spring MVC 中无法将有效的 JSON 数组作为请求正文

java - 异步不工作

java - Android - 自定义 ListView Viewholder

java - 为什么 Java 中的 LineNumberReader 总是返回零?

java - includeViewParams=true 不能与迭代集合的复合组件结合使用

spring - 如何在 Spring Data REST 中禁止 PUT 而允许 POST 和 PATCH?

java - 如何在 Spring MVC 中获取 JSON 作为响应?

java - 将 BufferedImage 对象作为文件保存到 Amazon S3