likes
comments
collection
share

Spring Boot中的一个坑:@EnableWebMvc注解导致的Jackson解析错误

作者站长头像
站长
· 阅读数 3

同事合了代码到开发分支,并没有涉及到改动的类却报错。错误信息如下:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; 
nested exception is org.springframework.http.converter.HttpMessageConversionException: 
Type definition error: [simple type, class com.tongweb.demo.bean.Registration];
nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException:
Cannot construct instance of `com.tongweb.demo.bean.Registration` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)

这个错误的大概意思就是jackson解析Registration类时出现了错误,说Registration类缺少默认构造函数。这个类是Controller中一个方法的入参。示例代码如下:

@PostMapping(path = "/instances", consumes = MediaType.APPLICATION_JSON_VALUE)  
public String register(@RequestBody Registration registration) {  
    Registration withSource = Registration.copyOf(registration).source("http-api").build(); 
    return withSource.toString();  
}

相关代码并不涉及到改动,起初我猜测是某些jar冲突引起的。但是查看了他合并过来的代码,也并未涉及到jar依赖的升级变更等。对于这个错误网上是有很多解决方案的,比如加个构造函数。但是不排查到导致问题的原因,这总归是个雷。于是我开启了排查过程。

1. 初步怀疑:jar冲突

首先,我怀疑问题可能与jar冲突有关。为了验证这一点,我创建了一个新项目,使用了相同的jar包,但并没有触发Jackson解析报错。这排除了jar冲突的可能性。

2.源码追踪:JacksonHttpMessageConvertersConfiguration

spring boot提供了一个Jackson的HTTP消息转换器的配置JacksonHttpMessageConvertersConfiguration,会创建一个MappingJackson2HttpMessageConverterMappingJackson2HttpMessageConverter会负责解析Registration类。特别说明Registration类只有有参构造函数,没有无参构造函数。

3. HttpMessageConverters加载顺序

HttpMessageConverters负责管理Spring Boot应用程序中使用的HttpMessageConverters。提供了一种向web应用程序添加和合并其他HttpMessageConverter的方便方法。 如果需要,可以向特定的附加转换器注册此bean的实例,否则将使用默认转换器。说白了就是Spring Boot应用程序中的各个转换就是在HttpMessageConverters中。

按理来说MappingJackson2HttpMessageConverter也是HttpMessageConverters中的一员。但是我跟踪源码发现MappingJackson2HttpMessageConverter并未出现在HttpMessageConverters中。跟踪源码WebMvcConfigurationSupport类,源码如下:


protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {  
    messageConverters.add(new ByteArrayHttpMessageConverter());  
    messageConverters.add(new StringHttpMessageConverter());  
    messageConverters.add(new ResourceHttpMessageConverter());  
    messageConverters.add(new ResourceRegionHttpMessageConverter());
    ...
}

这一段关键代码是把HttpMessageConverters添加到messageConverters。后续解析@RequestBody修饰的类就会从中选择对应的Converter。当源码进行到这一步的时候。MappingJackson2HttpMessageConverter并没有在这个过程中出现,导致它未被正确加载。

4.影响因素:@EnableWebMvc注解

@EnableWebMvc注解的作用是导入DelegatingWebMvcConfiguration类,这个类是一个@Configuration类,它会创建一个WebMvcConfigurationSupport类的实例,并将其注册到容器中。WebMvcConfigurationSupport类是Spring MVC的核心配置类,它会提供一些默认的组件,包括一些默认的HttpMessageConverter。但是,这些默认的转换器中并不包含MappingJackson2HttpMessageConverter,因为它需要依赖Jackson库,而Spring MVC并不强制依赖Jackson库。

Spring Boot的WebMvcAutoConfiguration类是一个@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)的类,它会在容器中没有WebMvcConfigurationSupport类的实例的时候才会被加载。这个类会提供一些自动配置的组件,包括MappingJackson2HttpMessageConverter。它会检测容器中是否有Jackson库,如果有的话,就会创建一个MappingJackson2HttpMessageConverter对象,并将其添加到HttpMessageConverters对象中。

当我们在应用程序中使用@EnableWebMvc注解时,就会导致容器中有一个WebMvcConfigurationSupport类的实例,这样就会触发@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)的条件,使得WebMvcAutoConfiguration类不会被加载。这样,MappingJackson2HttpMessageConverter就不会被创建,也不会被添加到HttpMessageConverters对象中。因此,@EnableWebMvc注解就导致MappingJackson2HttpMessageConverter没有被装配了。

5.结论及解决方案

因此,问题的关键在于使用了@EnableWebMvc注解。为了解决这个问题,可以采取以下方案:

  • 避免使用@EnableWebMvc注解:这样将使用Spring Boot的默认MVC配置,确保MappingJackson2HttpMessageConverter正确加载。
  • 手动配置Spring MVC功能:如果需要自定义MVC配置,可以手动配置拦截器、视图解析器、消息转换器等。这确保了MappingJackson2HttpMessageConverter被正确加载,并解决了Jackson解析错误。