为什么不应该再使用fastjson

zxdposter      2022-02-12     212

关键词:

背景

目前在国内环境中,应该是使用 FastJson 比较多,从国内的搜索到的文章教程中,大部分都是用的 FastJson,由此可见在国内的普及度还是较高的。


我一开始也是完全使用 FastJson 作为自己的主力库,主要有以下几点原因:

  1. 国人开发,国内使用的人较多,搜索起来比较方便。
  2. 使用起来简单方便,用起来只需要关心 JsonObject、JsonArray 两个类。
  3. 类型转换省心。
  4. Github 上 star 数很多。
  5. 一开始被人写文章安利,描述 FastJson 在很多场景下性能第一。


但是使用了一段时间后发现,FastJson 与其说好用,倒不如说在一些简单的小项目下使用起来较为方便,避免繁琐了的不少配置。


但是一旦项目多了起来,各个模块之间需要配合的时候,就避免不了对序列化框架进行配置,此时 FastJson 的短板开始暴露出来了。


最近研究许多了微服务的架构方面的事情,用到了 spring cloud gateway,spring boot security,spring boot data session,spring boot redis,等多个模块,从这些模块中可以看出来,很多东西都需要对数据进行序列化反序列化。

不使用的理由

在这里列举一些 不应该再使用 FastJson 的理由。

1、表现一致

spring 家族默认都是使用 jackson 去做序列化工作的。


spring cloud gateway 中使用的 webflux 对数据的序列化反序列化,你希望在 spring boot mvc 中有同样的表现,包括在你的代码里面,肯定需要针对这两种情况进行统一的配置,能够在时间格式、对 Null 值的处理、枚举类型上,有相同的表现。

如果你是这么想的,并且去付诸行动,那么面临你的就是必须去同时修改 spring cloud gateway 和 spring cloud mvc 的序列化配置。

其中的代码量不必多说,要面临的还包括稳定性兼容性等一系列的问题,这些问题都需要长时间的运行测试,来保证稳定性。


而使用 jackson,希望做到一致性配置,只需要把下面的代码放在每个模块里面,利用自动装载生效,不管是 webflux 还是 webmvc 都可以使用。

@Configuration
public class JacksonConfig {
    @Value("${spring.jackson.date-format:yyyy-MM-dd HH:mm:ss}")
    private String dateFormatter;

    @Bean
    @Primary
    Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
        JavaTimeModule module = new JavaTimeModule();

        LocalDateTimeDeserializer localDateTimeDeserializer = new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(dateFormatter));
        LocalDateTimeSerializer localDateTimeSerializer = new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(dateFormatter));
        module.addDeserializer(LocalDateTime.class, localDateTimeDeserializer);
        module.addSerializer(LocalDateTime.class, localDateTimeSerializer);

        return new Jackson2ObjectMapperBuilder()
                .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                .findModulesViaServiceLoader(true)
                .modulesToInstall(module);
    }
}

上面的代码能够生效的关键就是,spring 架构里面,已经默认实例化出了 ObjectMapper 这个 bean,这个 bean 有且只有一个。

2、在 spring boot security 中的兼容问题

上面说到 spring 默认都是使用 jackson,其中的 security 也不例外。

用到 spring boot security,最好是搭配 spring boot session 和 spring boot redis,这样能够将令牌直接存储 redis,但是默认情况下,序列化到 redis 中是使用 java 默认的序列化方式,但是这种方式无法直接给人查看,所以要自定义一个 RedisSerializer 去实现。

如果你使用 FastJson,有些类是没法直接序列化成实体的,比如 UserDetails 此类,security 中为了安全起见,实例化之后是没有 set 方法的,序列化只能通过构造函数来进行。

这样直接的使用 FastJson 的行不通的,只能自己来做去写相应的代码去做。

但是 security 依赖里面,直接针对 jackson 提供了 SecurityJackson2Modules 这个类,把所有你可能用到的 security 对象序列化方法都提供了。十分方便。


你还能自己仿造里面的类自己去实现扩展 UserDetails,只需要去模仿 UserMixinUserDeserializer 这两个类的写法,自己写一个 modules 即可。

3、FastJson 不严谨的漏洞

这里的漏洞并非是安全上的漏洞,而是逻辑上的。


比如,我希望某个枚举类是通过自定的构造函数传入值进行序列化的,而不是通过枚举中的 valueOf 用 enum 的 ordinal 或者 name 去做。

FastJson 不是没有方法去做,而是有非常让人很难理解的低级错误存在。


我定义了一个枚举类 Status。
在枚举类上使用@JSONType(deserializer = xxx.class)
把枚举类当成字段放在其它类 MyClazz 中,字段上使用@JSONField(deserializeUsing=xxx.class)
这两种方法全部用上,再使用 JSONObject.toJavaObjec(jsonObject, `MyClazz.class`),依然不会调用 xxx.class,的反序列化方法。
但是使用 JSONObject.parseObject(jsonObject.toJSONString(), MyClazz.class),却能够反序列化。
我跟踪过 FastJson 的源码,确实是逻辑上的疏忽,不是什么必要的原因忽略的。
更具体的原因可以去看一下 FastJson 中 TypeUtils 的源码。


这个问题,在 jackson 中完全是不存在的,只需要在枚举类中添加一个序列化和另外一个反序列化方法,分别加上注解 @JsonCreator@JsonValue,不仅不存在,使用起来还极为方面,不需要额外的类,并且在任何地方都能生效。


这种逻辑上的漏洞,出现在一个使用人数这么多的项目身上,有点不太应该。

4、FastJson 的代码质量不太好

这一点同样在源码中可以得到印证。


因为要做背景中提到的工作,时常翻看源码是肯定的,我发现 FastJson 中比较有代表性三个类。JSON、JSONObject、JSONArray这三个开发者最为常用的类,里面的源码感觉到有一些割裂。


JSON 最新的代码上,你能看到类的上面有板有眼写了不少注释,往下看,就能发现方法写了大量的注释,却有不少方法一点注释都没有。


而且 JSONObjectJSONArray 就更为拉胯了,你甚至找不到什么注释。虽然说它们使用起来比较简单,但是一点注释都没有,有点太说不过去了。


从上面我大概能推断出来一些问题,在一开始的版本中,注释工作,代码质量都比较注重一些,但是随着时间过去了,人员的变动可能比较大,主导人员渐渐放松了对代码质量的要求,逐渐演变成了今天这个样子。由此可见维护工作需要从一而终。

5、FastJson 的设计上有缺陷

此处设计上的缺陷是我与 jackson 比较后得出的,因为我没有接触过其它的 json 库。


FastJson 的思想比较简单,就是把所有的 Object 看做嵌套 HashMap,所有的 Array 看成 ArrayList。


在 FastJson 中,你只能看到一些简单的类型,比如 string、int、double、object 等,虽然这些基础类简单,使用起来也更加容易接受,但是问题是考虑细节不够全面。比如对 null 的处理,空的处理。这种方式也没法为程序带来更多的扩展性,在基础类的基础上去扩展,只能通过多加代码去判断,而不能通过面向对象的方式去解决问题。


反观 jackson 中,所有的基础类都被包装了起来,string 对应 TextNode,int 对应 IntNode,null 对应 NullNode 等等,这样虽然使用起来麻烦,但是带来的好处确实更加多了。其中一点就是不同类型之间的相互转化,明显扩展性起来更加灵活。


使用 jackson 代替 FastJson

虽然 FastJson 不推荐使用,但是要承认的是,用起来确实非常省心方便,开箱即用,用起来也比较轻量。


不过这个优点也是能通过封装 jackson 来达到同样的目的。


具体思路就是用类似用代理的方式,定义 FastJson 中的方法,但是内部实现方式却是用 jackson。


这里提给我的项目地址,里面已经对 jackson 做了类似于 fastsjon 封装,并且提供了一些比 fastjson 更方便的函数做补充。如果对 ObjectMapper

最后

说到这里,一句话就是,使用 jackson 确实比 fastjson 要更加好。


这里再说几点我自己发现的小细节

  1. FastJson 项目很多地方并没有遵守 alibaba 的代码规范,不管从命名 JSONObject 还是里面的源码。
  2. alibaba 内部项目似乎也没有使用 FastJson,我引用了 nacos 和 arthas 两个项目,都没有发现使用 FastJson 的痕迹。使用的也是 jackson,在 nacos-common 包中的 JacksonUtils 类中。
  3. alibaba 的开源项目并没有外人吹捧的质量那么高,我最近发现相同的一个大项目 nacos 中两个不同的小模块,一个是配置,一个是日志,源码里面读取配置的方式都不太对。并且 nacos 没有考虑过多个项目同时使用 nacos,运行在同一个环境下面,日志路径冲突的问题,我最后只能通过环境变量里面的 PID 去区分目录。

Java 9 中不推荐使用 Observer。我们应该使用啥来代替它?

...22:33:00【问题描述】:Java9出来了,Observer已被弃用。这是为什么?这是否意味着我们不应该再实现观察者模式了?最好知道什么是更好的选 查看详情

你怎么称呼一个旧的并且不应该再使用的方法或库?

】你怎么称呼一个旧的并且不应该再使用的方法或库?【英文标题】:Whatdoyoucallamethodorlibrarywhichisoldandshouldn\'tbeusedanymore?【发布时间】:2011-03-1906:48:22【问题描述】:必须有一个名称。我在考虑退化或未使用(旧的描述性不够)... 查看详情

fastjson中对象转换中看到的一些现象(代码片段)

...一看代码如下:JSON.parseObject(value,List.class)这里使用了fastjson,但是这里一看就看的出来,使用的不是很流畅。先讲讲fastjson什么是fastjsonfastjson是阿里开源的一个json操作库。从名字就可以看出来。它支持json和java对象类... 查看详情

fastjson中对象转换中看到的一些现象(代码片段)

...一看代码如下:JSON.parseObject(value,List.class)这里使用了fastjson,但是这里一看就看的出来,使用的不是很流畅。先讲讲fastjson什么是fastjsonfastjson是阿里开源的一个json操作库。从名字就可以看出来。它支持json和java对象类... 查看详情

什么时候不应该使用关系数据库? [关闭]

...除了google/bigtable场景,什么时候不应该使用关系数据库?为什么不,你应该使用什么?(你学会了“艰难的方式”吗?)【问题讨论】:当您的架构变化很大时,您将很难使用关系数据库。这是XML数据库或键值对数据库最适合的... 查看详情

什么时候不应该使用规则引擎? [关闭]

】什么时候不应该使用规则引擎?[关闭]【英文标题】:WhenshouldyouNOTuseaRulesEngine?[closed]【发布时间】:2010-10-2023:36:15【问题描述】:我有一个相当不错的使用规则引擎的优点列表,以及使用它们的一些原因,我需要的是你不应该... 查看详情

为啥 DefaultMessageListenerContainer 不应该使用 CachingConnectionFactory?

】为啥DefaultMessageListenerContainer不应该使用CachingConnectionFactory?【英文标题】:WhyDefaultMessageListenerContainershouldnotuseCachingConnectionFactory?为什么DefaultMessageListenerContainer不应该使用CachingConnectionFactory?【发布时间】:2014-03 查看详情

fastjson2为什么这么快?(代码片段)

导读本文作者从以下三个方面讲述了fastjson2使用了哪些核心技术来提升速度。1、用「Lambda生成函数映射」代替「高频的反射操作」2、对String做零拷贝优化3、常见类型解析优化fastjson是很多企业应用中处理json数据的基础工具࿰... 查看详情

SHTML 和 DHTML - 应该使用它们,为啥不使用它们?

...ndDHTML-Shouldtheybeused,whyaren\'tthey?SHTML和DHTML-应该使用它们,为什么不使用它们?【发布时间】:2018-06-1618:13:23【问题描述】:随着HTML5的发布,我们是否应该继续使用.dhtml和.shtml文件格式?为什么即使我们使用HTML而没有DHTML,Js和CS... 查看详情

什么时候应该使用 Oracle 的索引组织表?或者,我什么时候不应该?

】什么时候应该使用Oracle的索引组织表?或者,我什么时候不应该?【英文标题】:WhenshouldIuseOracle\'sIndexOrganizedTable?Or,whenshouldn\'tI?【发布时间】:2011-03-2321:56:37【问题描述】:索引组织表(IOT)是存储在索引结构中的表。而存储... 查看详情

为什么不应该使用数据库外键(重温旧文)

为什么不应该使用数据库外键(重温旧文)导读:最近一篇是否应该使用数据库外键的旧文在国外技术网站HackerNews上引发了热议。文章的作者是一名GitHub工程师提出,观点是不应该使用数据库外键。反方的主要观点是互联网开... 查看详情

为啥不应该使用 Number 作为构造函数? [复制]

...制]【英文标题】:WhyshouldyounotuseNumberasaconstructor?[duplicate]为什么不应该使用Number作为构造函数?[复制]【发布时间】:2010-09-2700:30:30【问题描述】:我在JSLint中输入了这条语句:varnumber=newNumber(3);并收到以下消息:不要使用Number... 查看详情

为啥我不应该使用 Unity?

...啥我不应该使用Unity?【英文标题】:Whyshouldn\'tIuseUnity?为什么我不应该使用Unity?【发布时间】:2010-12-2209:59:03【问题描述】:我正在使用UnityIoC容器。这真的不是我做出的决定,它只是与Prism一起出现的,我一直坚持下去。我... 查看详情

为啥不应该在函数式编程中使用变量赋值

...文标题】:Whyvariableassignmentsshouldnotusedinfunctionalprogramming为什么不应该在函数式编程中使用变量赋值【发布时间】:2017-07-0119:11:20【问题描述】:我正在学习函数式编程,我可以理解为什么不可变比可变对象更受欢迎。This的文章... 查看详情

什么时候不应该在 .Net 中使用 ThreadPool? [关闭]

...adPool?看起来最好的选择是使用线程池,在这种情况下,为什么它不是唯一的选择?您对此有何经验?【问题讨论】:【参考方案1】:@Eric,我不得不同意Dean的看法。线 查看详情

为啥不应该使用 `'` 来转义单引号?

...【英文标题】:Whyshouldn\'t`'`beusedtoescapesinglequotes?为什么不应该使用`\'`来转义单引号?【发布时间】:2011-01-0605:25:53【问题描述】:如WhendidsinglequotesinHTMLbecomesopopular?和Jqueryembeddedquoteinattribute中所述,HTML上的***条目说 查看详情

为啥不应该使用 `'` 来转义单引号?

...【英文标题】:Whyshouldn\'t`'`beusedtoescapesinglequotes?为什么不应该使用`\'`来转义单引号?【发布时间】:2011-01-0605:25:53【问题描述】:如WhendidsinglequotesinHTMLbecomesopopular?和Jqueryembeddedquoteinattribute中所述,HTML上的***条目说 查看详情

fastjson反序列化漏洞(代码片段)

Fastjson反序列化漏洞目录Fastjson反序列化漏洞一、Fastjson介绍1、什么是fastjson?2、fastjson的优点二、影响范围:三、漏洞原理四、漏洞利用五、漏洞发现六、漏洞修复一、Fastjson介绍1、什么是fastjson?fastjson是阿里巴巴的开源JSON解... 查看详情