记一次Spring中HttpMessageConverter的源码分析
阅读本文大概需要 3 分钟。
最近在使用Spring时遇到一个关于JSON解析的问题,@Response的接口如果返回值为一个Interfacce那么结果将变为空对象,也就是{},记录一下,防止再次踩坑。
前两天,业务部门反映,官网有新闻数据接口返回数据为空,导致官网无法访问。于是我着手开始查找原因。
1.
当然是首先怀疑是不是代码出错导致JSON返回了空对象,于是我直接debug了一下controller的代码,直接call到返回值那一行,发现返回值到响应时还是正常的,可以确定代码是没有问题的,排除。
2.
排除了业务代码问题后,我的注意力放在了项目中的几个拦截器上,会不会是拦截器导致的数据被刷掉了?
进行逐一排查,但是奇怪的是拦截器并没有做什么修改接口响应的操作,仅仅是记录日志和一些无关紧要的操作
3.
到这一步,问题的原因已经超出了搜索引擎和个人经验能解决的范畴了,于是我开始翻代码提交记录,试图找出影响接口响应值的修改。
由于ResponseBody注解和JSON解析框架有着密切的关系,所以着重排查有关JSON的依赖引用,经过我的排查,发现jackson依赖在最近的提交中被删除。
问题的原因浮出水面,Jackson的引用被删除,导致Spring默认的HttpMessageConverter由Jackson变为了默认的Gson。
Gson解析的 ”BUG“ (姑且称为BUG,后面会解析)导致对象解析失败,所以响应变为了{}
问题原因找到后,添加上Jackson依赖,测试,响应正常
虽然问题解决,但是我还是想要尝试去探究问题的原因
因为知道了是由于HttpMessageConverter的JSON解析器导致,所以我直接跟踪代码定位到解析器执行部分。
1.
AbstractMessageConverterMethodProcessor 类
核心代码:
这里循环了所有的HttpMessageConverter也就是消息处理器,每个HttpMessageConverter都会有一个canWrite方法,来确认是否执行。
当所有条件都满足时,会进入 HttpMessageConverter的write方法,也就是我用红框圈起来的代码。
2.
继续跟踪会进入AbstractGenericHttpMessageConverter类的write方法,这个类是消息处理器的基类,我们能看到这个方法处理了StreamingHttpOutputMessage类型,随之调用了子类的writeInternal方法。
3.
继续跟踪代码进入具体的Gson解析器实现类GsonHttpMessageConverter的writeInternal方法,代码如下
OK,到这一步,已经完全定位到了导致响应为{}的原因所在,再来看 继续分析
4.
这里调用了Gson的toJson方法,并且传入了源对象,对象Type类型,以及一个输出流,这里需要注意的是传入的Type类型是返回值的类型也就是一个接口,这样做有什么后果呢?继续进入toJson方法
首先,这个方法的核心是根据传入的type类型构建了一个Adapter对象
5.
就是它!胜利在眼前,我们进入~
这个方法看起来有点复杂,没关系,大家只关注我圈起来的核心部分,也就是真正的构造部分,这一句会创建一个TypeAdapter对象,现在查看其代码
这里很简单,就是获取一下全部的字段然后创建一个Adapter对象,但是来再看getBoundFields方法
我们看到这里会判断type如果是一个接口便不会往下执行了,也就是说这个Adapter的字段列表将是空,空对象生成出来的Json是{}也就是必然结果了~
分析完毕,一开始我以为是Gson的BUG,后来慢慢分析发现这是Spring中GsonHttpMessageConverter 实现类的 BUG....
附上我提交的issues链接:
https://github.com/spring-projects/spring-framework/issues/24234
本Demo源码链接:
https://github.com/iwangjie/GsonTest
如果你喜欢这篇文章,再看,转发+关注。
生活很美好,明天见(。・ω・。)ノ♡
相关文章
- Spring Framework 源码学习笔记(六)
- Spring读源码系列番外篇09--BeanWrapper的应用
- spring官网下载[通俗易懂]
- Spring+MyBatis实例详解「建议收藏」
- 【09】Spring源码-分析篇-DI源码分析
- 解释spring框架中bean的生命周期_Spring bean的生命周期
- 【11】Spring源码-分析篇-事务源码分析
- 微服务组件-----Spring Cloud Alibaba 注册中心 Nacos源码(1.4.x版本)分析
- mybatis-spring源码解析--事务详解
- 从源码看Spring的i18n·优雅的国际化实战
- BeanFactoryPostProcessor-spring源码详解(三)
- SpringAOP(2)-spring源码详解(七)
- Spring - 事件监听机制 源码解析
- Spring源码核心知识点凝练总结
- 【Spring源码】- 10 Spring AOP核心API
- rocketmq-spring : 实战与源码解析一网打尽
- Spring Security 中使用Keycloak作为认证授权服务器
- 【Spring专题】「技术原理」从源码角度去深入分析关于Spring的异常处理ExceptionHandler的实现原理
- Spring MVC 执行流程和源码分析详解编程语言
- Spring AOP详解所需要的包编程语言
- Spring IOC/BeanFactory/ApplicationContext的工作流程/实现原理/初始化/依赖注入源码详解编程语言
- Spring Boot2.0之Admin-UI分布式微服务监控中心详解编程语言
- spring Boot(十九):使用Spring Boot Actuator监控应用详解编程语言
- Spring事务管理接口:PlatformTransactionManager、TransactionDefinition和TransactionStatus
- Spring setUsername方法:设置访问数据库的用户名