慎用@EnableWebMvc注解

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

java 复制代码
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中一个方法的入参。示例代码如下:

java 复制代码
@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依赖的升级变更等。对于这个错误网上是有很多解决方案的,比如加个构造函数。但是不排查到导致问题的原因,这总归是个雷。于是我开启了漫长的排除过程。

首先考虑是jar冲突导致。我重新写了一个新项目,沿用了我目前的所有jar。访问instances接口,jackson解析并不会报错。那只有跟踪源码了。

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

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

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

java 复制代码
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并不存在。它的加载顺序发生在了addDefaultHttpMessageConverters方法执行之后。这就导致messageConverters中并没有MappingJackson2HttpMessageConverter。所以当@RequestBody修饰的类没有无参构造函数的时候解析就报错了。一个正常的应用程序MappingJackson2HttpMessageConverter会优先被加载。找到问题的原因就方便多了。

为什么JacksonHttpMessageConvertersConfiguration的加载顺序会晚于WebMvcConfigurationSupport类呢?换而言之有哪些因素会影响WebMvcConfigurationSupport类的加载。答案就是@EnableWebMvc注解!!

如果在代码中使用了@EnableWebMvc注解,那么WebMvcConfigurationSupport就会比JacksonHttpMessageConvertersConfiguration优先加载,因为@EnableWebMvc注解会导入DelegatingWebMvcConfiguration类,而这个类继承了WebMvcConfigurationSupport类,并且有@Order注解,指定了加载顺序。

这样的话,Spring Boot的默认MVC配置就会被关闭,你需要自己配置Spring MVC的功能,例如拦截器、视图解析器、消息转换器等。

相关推荐
做个文艺程序员几秒前
Spring Boot 项目集成 OpenClAW【OpenClAW + Spring Boot 系列 第1篇】
java·人工智能·spring boot·开源
曹牧37 分钟前
Spring:@RequestMapping
java·后端·spring
霸道流氓气质43 分钟前
SpringBoot+LangChain4j+Ollama实现本地大模型语言LLM的搭建、集成和示例流程
java·spring boot·后端
刘 大 望2 小时前
RAG相关技术介绍及Spring AI中使用--第三期
java·人工智能·后端·spring·机器学习·ai·aigc
MegaDataFlowers2 小时前
使用SpringBoot+MyBatis+MySQL完成后端的数据库增删改查(CRUD)操作
数据库·spring boot·mybatis
做个文艺程序员2 小时前
Spring Boot 封装 OpenClAW 服务层最佳实践【OpenClAW + Spring Boot 系列 第2篇】
java·人工智能·spring boot·开源
2601_949816682 小时前
如何在 Spring Boot 中配置数据库?
数据库·spring boot·后端
做个文艺程序员3 小时前
多轮对话与会话管理:构建上下文感知的 AI 接口【OpenClAW + Spring Boot 系列 第4篇】
人工智能·spring boot·开源
tang_jian_dong4 小时前
springboot + vue3 集成tianai.captcha验证码
java·spring boot·spring
indexsunny5 小时前
互联网大厂Java面试实录:微服务+Spring Boot在电商场景中的应用
java·spring boot·redis·微服务·eureka·kafka·spring security