关于 FastJson 执行反序列化时出现变量遗漏的坑

4 minute

前言

使用 fastjson 有很多坑,这里记录一个 debug 时比较难发现的坑,同时还记录了 lombok 的一些坑。

FastJson 反序列化流程

fastjson 反序列化对象时,首先会找到该类的构造方法,如果有无参构造,将会根据 set 方法进行反序列化,如果没有将会根据一个最长的有参构造方法进行反序列化。

这是 fastjson 反序列化的流程图:

fastjson 反序列化流程

(photo from here)

具体场景分析

假如我们有一个场景,某个对象 child 继承了一个父对象 father,father 含有一些参数,child 也含有自己独有的一些参数,对 child 进行反序列化时,所有的参数(包括 father 的参数)都需要反序列化,否则将导致后续逻辑处理出现参数遗漏的问题。

而我们经常使用 lombok 来简化构造方法和 set 方法等的编写,这里列出几种可能的情况:

对于 child,如果我们只通过 @AllArgsConstructor 进行构造,而 lombok 该注解所生成的构造函数并不包含 father 的参数,那么会导致反序列化时的参数遗漏;

在上一步的基础上,如果我们加上 @NoArgsConstructor,但是没有 set 方法,则无法成功反序列化;

在上一步的基础上,我们再加上 @Data,而且注意 father 和 child 都需要添加该注解,这时才可以成功不遗漏参数的反序列化。(注:father的 set 方法 child 可以继承到,但 father 的构造方法 child 不会继承)。

如果我们平时都是习惯性的加上 @AllArgsConstructor,@NoArgsConstructor,@Data 这几个注解,那么就很难遇到 fastjson 反序列化时的问题,但一旦遇上了会比较难发现。

假如有这么一个场景,我们需要在初始化 child 时,father 中的参数都是必要的(可能添加了 @NotNull 之类的注解),为确保忘记设置这些参数,我们可能会禁止 child 的空参构造,同时编写一个包含了 father 必要参数的构造方法。

这个时候问题就来了,假如你又添加了 @AllArgsConstructor 注解,那么 fastjson 反序列化时就会使用参数最多的构造方法,导致 father 的必要参数或者 child 的参数遗漏,如果不添加 @AllArgsConstructor 注解,那么就是 child 的参数遗漏了。

所以这时候为了确保不遗漏任何参数地进行反序列化,需要在 child 中再构造一个包含了 child 自己的所有参数和 father 必要参数的构造方法,以保证成功地反序列化。

总结

对一些框架的使用不能不知其所以然地去使用,未必要对底层实现很清楚,但至少要知道使用的这个东西具体做了什么,在某些场景下会不会有问题。

比如 lombok 的注解到底生成了什么代码,在某种情况下(比如在继承的场景下)会不会遗漏。再比如 fastjson 是依据的什么进行序列化和反序列化。如果不清楚这些比较基本的东西就盲目地使用,那 debug 的时候就会比较迷茫了。