news 2026/9/21 2:37:02

从Fastjson 1.x迁移到Fastjson2:性能、安全与API兼容性实践指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从Fastjson 1.x迁移到Fastjson2:性能、安全与API兼容性实践指南

1. 为什么要从 Fastjson 1.x 迁到 Fastjson2:先看清楚这笔账

先说个很多人没意识到的事实:Fastjson2 并不是 Fastjson 1.x 的简单小版本升级,而是一次底层重写。官方在发布时就把包名、API 结构都做了调整,com.alibaba.fastjson变成了com.alibaba.fastjson2,光是这一点就注定“改个版本号就完事”的偷懒方案走不通。

那为什么还要迁?我在实际项目中感受到最明显的问题是性能和安全性。先说性能,Fastjson2 在序列化和反序列化上的吞吐量相比 1.x 有明显提升,尤其是在大对象、复杂嵌套结构、高频调用的场景下,差距能拉开一个量级。我们内部做过一轮压测,同样一个包含 30 个字段、嵌套两层的数据对象,Fastjson2 的序列化耗时大概是 1.x 的 60% 左右,GC 压力也更小。对于高并发接口来说,这属于那种“不迁不知道,一迁回不去”的提升。

再说安全性。Fastjson 1.x 这些年曝出的反序列化漏洞比较多,AutoType 机制虽然功能强大,但也成了攻击面。Fastjson2 在默认配置下对 AutoType 的限制更严格,不再像以前那样默认开启自动类型装配,而是需要显式声明。这不仅仅是一个版本号的变化,它直接影响你的代码能不能继续跑、怎么跑。

所以在我看来,迁移的核心动机就两个:一是性能收益,二是安全兜底。如果只是抱着“反正能跑就不动”的心态,等哪天线上环境被迫升级或者审计强制要求的时候再动手,代价只会更大。毕竟越晚迁移,老代码积压得越多,兼容性排查的难度越大。

想要把这个迁移做得平滑,你需要对以下几件事有清楚的认知:Fastjson2 的包名和依赖变化、常见 API 的替换方案、配置项的差异、以及那些隐藏在角落里的坑。下面我一个一个拆开讲。

提示:如果你的项目还在使用 Fastjson 1.2.x 的较老版本,建议先升级到 1.2.83 以上再开始迁移,这样能减少一些早期版本的边界问题干扰。

2. 依赖切换与包名变化的处理:先把基础环境弄干净

2.1 Maven/Gradle 依赖怎么换

迁移的第一步,自然是从依赖层把 Fastjson2 引进来。Maven 项目在pom.xml中把原来的 fastjson 依赖替换掉即可:

<dependency> <groupId>com.alibaba.fastjson2</groupId> <artifactId>fastjson2</artifactId> <version>2.0.53</version> </dependency>

Gradle 对应写法:

implementation 'com.alibaba.fastjson2:fastjson2:2.0.53'

这里版本号不是随便挑的,我建议去 Maven 中央仓库看最新的稳定版,不要直接用 2.0.x 的早期版本,因为 Fastjson2 在 2.0.1x 之后修了不少序列化相关的边界 bug。选择较新的稳定版能少踩很多坑。

还有一个必须注意的点:如果你原来用了 Fastjson 1.x 的fastjson依赖,同时又需要兼容老代码,可以考虑用官方提供的兼容包fastjson1-compatible。这个兼容包的包名还是com.alibaba.fastjson,但它内部实际上是 Fastjson2 的实现。适合那种“没时间快速改完所有 import,但又被要求用 Fastjson2 提升性能和安全性”的中间过渡场景。

<dependency> <groupId>com.alibaba.fastjson2</groupId> <artifactId>fastjson2-extension-spring5</artifactId> <version>2.0.53</version> </dependency>

不要急着把兼容包当成终态方案,它只能帮你过渡。长期来看,代码里混着新旧两套包名会把团队搞晕,后续维护起来非常痛苦。

2.2 import 路径的全量替换方案

Fastjson2 官方设计了一个非常贴心的机制:com.alibaba.fastjson下的核心类在 Fastjson2 中仍然存在,但是需要换成com.alibaba.fastjson2。对于大部分基础用法,代码逻辑可以基本保持不变,变的只是 import 语句。

以最常见的几个类为例,对应关系如下:

旧包名(Fastjson 1.x)新包名(Fastjson2)
com.alibaba.fastjson.JSONcom.alibaba.fastjson2.JSON
com.alibaba.fastjson.JSONObjectcom.alibaba.fastjson2.JSONObject
com.alibaba.fastjson.JSONArraycom.alibaba.fastjson2.JSONArray
com.alibaba.fastjson.JSONExceptioncom.alibaba.fastjson2.JSONException
com.alibaba.fastjson.TypeReferencecom.alibaba.fastjson2.TypeReference
com.alibaba.fastjson.annotation.JSONFieldcom.alibaba.fastjson2.annotation.JSONField
com.alibaba.fastjson.serializer.SerializerFeaturecom.alibaba.fastjson2.JSONWriter.Feature

如果项目代码量很大,手动替换 import 不现实。我推荐的姿势是:先用 IDE 的全局替换功能做一轮包名替换,然后编译看报错,根据报错逐个处理差异点。这样比纯手工改要快很多,也不容易漏。

但是要特别注意,Fastjson2 还有一个特性就是同时提供了兼容包,为了让 1.x 用户平滑迁移,Fastjson2 的代码里你可以写com.alibaba.fastjson.JSON也能跑,只要你引入的是fastjson1-compatible模块而不是原版 fastjson2 模块。这里有点绕,我需要说清楚:

  • com.alibaba.fastjson2:fastjson2:只能用com.alibaba.fastjson2包。
  • com.alibaba.fastjson2:fastjson1-compatible:可以用com.alibaba.fastjson包,但底层也是 Fastjson2 的实现。

我实际使用下来,建议优先采用第一种,直接切包名,虽然改动量大,但不会有暗坑。第二种适合那些无法快速回归、只想降风险的场景。

3. 核心 API 迁移对照:JSON.parseObject、toJSONString 等方法的兼容性差异

3.1 最常用的 parseObject 和 toJSONString

大部分项目对 Fastjson 的使用都集中在JSON.parseObject()JSON.toJSONString()这两个方法上。好消息是,Fastjson2 对这两个方法的签名做了很好的兼容,绝大多数情况下你只管换包名,代码不需要动。

比如以下代码在 Fastjson2 中依然有效:

String jsonStr = "{\"name\":\"张三\",\"age\":30}"; JSONObject obj = JSON.parseObject(jsonStr); String name = obj.getString("name"); int age = obj.getIntValue("age");

以及:

User user = JSON.parseObject(jsonStr, User.class); String json = JSON.toJSONString(user);

这类基础用法在 Fastjson2 中对应的仍然是JSON.parseObject(String, Class)JSON.toJSONString(Object)。它们的底层实现虽然是重写的,但对外行为一致,所以业务代码里如果只是这么用,迁移成本几乎为零。

3.2 parseObject(String, TypeReference) 的差异

复杂泛型场景下,Fastjson1 的写法如下:

Map<String, List<User>> map = JSON.parseObject(jsonStr, new TypeReference<Map<String, List<User>>>() {});

Fastjson2 也支持同样的写法,仍然使用TypeReference。但是要注意,TypeReference所在的包从com.alibaba.fastjson变成了com.alibaba.fastjson2,所以import需要同步更换。

我在实际迁移过程中遇到过一个比较隐蔽的差异:当泛型嵌套层级较深时,Fastjson2 对泛型的解析在某些边界情况下会走不同的解析逻辑。具体来说,像Result<PageData<User>>这种三层泛型嵌套,Fastjson1 偶尔会出现类型丢失问题,反序列化出来的列表里面的对象是 JSONObject 而不是 User。Fastjson2 在泛型保留上做得更好,基本不会再出现这类问题。但如果你的业务里依赖 Fastjson1 那种“类型丢失但还能打印出对象”的容错,这次迁移反而可能暴露出原先被掩盖的类型转换错误。

3.3 JSONObject 的 getJSONObject / getJSONArray

JSONObject 的日常操作,在 Fastjson2 中变化也比较小:

JSONObject parent = JSON.parseObject(jsonStr); JSONObject child = parent.getJSONObject("child"); JSONArray arr = parent.getJSONArray("list"); String name = parent.getString("name");

这些 API 在 Fastjson2 中依然存在,返回类型不变。不过要提醒一点:Fastjson2 对 JSONObject 的 getString、getIntValue 等方法的 null 处理做了更严格的校验。比如,如果 key 对应的值是null,Fastjson1 的getString可能直接返回 null,而 Fastjson2 在传入默认值或某些重载方法时会有不同表现。这本身不是什么大问题,但迁移后建议跑一遍单元测试,专门验证字段缺失、值为 null 的情况。

3.4 toJSONString 的序列化特性变化

说到序列化,Fastjson1 中控制序列化行为主要靠SerializerFeature,比如:

JSON.toJSONString(user, SerializerFeature.WriteDateUseDateFormat);

Fastjson2 中这个写法变成了:

JSON.toJSONString(user, JSONWriter.Feature.WriteDateUseDateFormat);

功能上是一一对应的,但枚举类从SerializerFeature换成了JSONWriter.Feature。以下是高频使用特性的对照:

Fastjson1 SerializerFeatureFastjson2 JSONWriter.Feature作用
WriteDateUseDateFormatWriteDateUseDateFormat日期格式化为字符串
WriteMapNullValueWriteNulls输出值为 null 的字段
WriteNullStringAsEmptyWriteNullStringAsEmptynull 字符串输出为 ""
DisableCheckSpecialChar无直接对应禁用特殊字符检查
PrettyFormatPrettyFormat格式化输出

注意表格里我标注了“无直接对应”的项。DisableCheckSpecialChar在 Fastjson2 中没有同名枚举,如果代码里用到了,需要仔细看它原本的语义再调整。这不算高频操作,但我见过有项目用它处理特殊字符场景,迁移时容易卡住。

序列化这块还有一个行为差异值得大家注意:Fastjson2 默认的序列化顺序和字段命名策略基本保持一致,但对于 getter 方法的识别,Fastjson2 会更规范地遵循 JavaBean 规范。举个例子,如果某个类里有一个isActive()方法但active字段不存在,Fastjson1 可能会把active作为一个虚拟属性序列化出去,Fastjson2 在部分配置下不会这么做。这会导致序列化出的 JSON 少了一个字段。遇到这种情况,要么在 getter 上显式加@JSONField注解,要么调整配置允许这类虚拟属性输出。

这里引出的核心教训是:不要只盯着编译是否通过,要重点做序列化结果对比,把迁移前后的 JSON 输出放到 diff 工具里比对,字段缺失、顺序变化都能一眼发现。

4. 配置项差异详解:SerializerFeature、Feature、AutoType 前后的不同用法

4.1 SerializerFeature 如何映射到 JSONWriter.Feature

前面列举了高频特性的对应关系,这里我再展开讲讲 Fastjson2 在配置方式上的整体思路。

Fastjson1 里,序列化配置主要靠SerializerFeature枚举加在toJSONString的参数里:

String json = JSON.toJSONString(data, SerializerFeature.WriteMapNullValue);

Fastjson2 里,类似的枚举换成了JSONWriter.Feature,但用法稍有不同。Fastjson2 同时支持在JSON.toJSONString方法参数中直接传 Feature:

String json = JSON.toJSONString(data, JSONWriter.Feature.WriteNulls);

也支持在具体对象上配置全局或局部 JSONWriter 上下文。个人建议还是用方法参数的方式,直观且局部可控,不污染全局设置。

序列化过程中,如果你的需求是“所有值为 null 的字段也要输出”,我在迁移中发现了一个容易踩的细节:Fastjson2 有两个看起来很像的枚举——WriteNullsWriteMapNullValue。在大多数场景下,WriteNulls是全字段生效的开关,而WriteMapNullValue是专门控制 Map 类型中 null 值的输出。如果你的对象中字段直接为 null,用WriteNulls才对。Fastjson1 时代很多人习惯了WriteMapNullValue不管三七二十一直接加,到了 Fastjson2 你会发现 Bean 字段的 null 值没按预期输出,就是这个原因。

4.2 反序列化 Feature 的差异

反序列化控制枚举在 Fastjson1 是Feature,在 Fastjson2 中对应的是JSONReader.Feature。比如:

JSON.parseObject(jsonStr, User.class, Feature.SupportNonPublicField);

换成 Fastjson2:

JSON.parseObject(jsonStr, User.class, JSONReader.Feature.SupportNonPublicField);

Feature 类的迁移路径很清楚,但要注意的是某些 Feature 在 Fastjson2 中改名了。举一个典型例子:

  • Fastjson1Feature.SupportClassForName在 Fastjson2 中对应JSONReader.Feature.SupportClassForName
  • Fastjson1Feature.IgnoreNotMatch在 Fastjson2 中对应JSONReader.Feature.IgnoreNullFields(语义不完全一致,需要确认)

我在迁移时遇到一个业务场景:前端传过来的 JSON 比后端 Bean 多几个字段,Fastjson1 默认忽略未知字段,Fastjson2 在某些严格配置下可能直接抛异常。如果你不想让多字段的情况报错,需要在 parse 时显式加上JSONReader.Feature.IgnoreNullFields或相关忽略未知字段的配置。

4.3 AutoType 机制的差异与安全性增强

这是迁移中很多人最头疼的部分。Fastjson1 的 AutoType 默认关闭,但之前很多项目为了某些多态场景会打开它,比如把子类信息写进@type字段。Fastjson2 在默认情况下也是关闭 AutoType 的,想要开启需要显式调用:

JSONFactory.getDefaultObjectReaderProvider().addAutoTypeBeforeHandler(handler);

或者通过:

JSON.config(Fastjson2Feature.SupportAutoType);

整体思路是:你必须要告诉 Fastjson2 允许哪些类参与自动类型装配,而不是像以前那样给一个全局开关就完事。

如果你的业务确实需要反序列化多态对象,我建议不要图省事直接全局开启 AutoType。正确做法是维护一个允许自动装配的类白名单,使用addAutoTypeBeforeHandler只放行需要的那几个类。既满足业务需要,又把安全风险控制在最小范围。

注意:在 Fastjson2 中,AutoType 的安全校验逻辑比 1.x 严格很多。如果你发现升级后某些反序列化场景报错,报错信息里提到了 AutoType 或supportAutoType,十有八九是类没有被加入白名单导致的。别慌,这不是 bug,是安全设计。

4.4 全局配置的迁移方式

Fastjson1 中有人会用SerializerFeature设置一个全局配置,比如在某些 Spring Boot 项目中通过自定义FastJsonConfig来设置序列化规则。Fastjson2 中对应的做法是使用JSONWriter.FeatureJSONReader.Feature的上下文配置。

这里有一个体感比较明显的坑:Fastjson2 默认对特殊字符的处理比 Fastjson1 更加严格。比如,当 JSON 字符串里包含某些控制字符或非法转义序列时,Fastjson1 可能帮你自动容错,Fastjson2 则会直接抛出异常。对于这种情况,我的建议不是去关闭严格校验去兼容脏数据,而是先检查数据源为什么会出现非法字符。你可以在 parse 阶段捕获 JSONException,记录下异常时的 payload 片段,方便后续定位脏数据来源。

说句实在话,全局配置最好保持一个原则:能用局部参数解决的,就不要开全局开关。全局开关一多,后续排查问题和迁移的难度都会指数级上升。

5. 注解与扩展机制迁移:JSONField、JSONType、ValueFilter 等使用变化

5.1 @JSONField 注解的兼容性

@JSONField 注解在 Fastjson2 中依然存在,包名变了之外,大部分字段属性的用法保持一致。常见的:

public class User { @JSONField(name = "user_name") private String userName; @JSONField(format = "yyyy-MM-dd HH:mm:ss") private Date createTime; @JSONField(serialize = false) private String password; }

这些写法在 Fastjson2 中仍然有效。不过有一个细节需要注意,Fastjson2 中 @JSONField 注解的ordinal属性在控制字段输出顺序时的行为比 Fastjson1 更严格。跨版本迁移后,如果原先依赖 ordinal 控制顺序,需要重新验证一遍字段顺序是否符合预期。

另外,Fastjson2 对 @JSONField 的deserializeUsingserializeUsing支持度保持得不错,如果你使用了自定义序列化器,通常只需要改 import 包名就能正常运行。

5.2 @JSONType 注解的变化

@JSONType 在 Fastjson1 中可作为类级别注解,用来指定序列化器、反序列化器以及 includes/excludes。Fastjson2 依然支持 @JSONType,但某些属性的底层处理逻辑有变化。

一个我在实践中遇到的差异是:@JSONType(ignores = {...})属性在 Fastjson1 中是忽略指定字段,Fastjson2 中也是一样。但如果你在类继承的父类中定义了 @JSONType,Fastjson2 对父类注解的继承和识别规则与 Fastjson1 略有差别。换个说法:同样的父子结构,Fastjson1 下父类注解能被子类“继承”生效,Fastjson2 下可能只对子类自身的字段生效。测试时要把继承场景覆盖到,不能只测平铺的类结构。

5.3 ValueFilter / NameFilter / BeforeFilter 的替代方案

Fastjson1 中常见的过滤器写法:

ValueFilter filter = (object, name, value) -> { if (value == null) { return ""; } return value; }; String json = JSON.toJSONString(user, filter);

Fastjson2 中依然支持ValueFilterNameFilterBeforeFilter这些接口,但它们对应的包路径变成了com.alibaba.fastjson2.filter。使用方式基本不变,只是 import 要换。

不过有一点体验上的差异:Fastjson2 中过滤器的调用时机和 Fastjson1 不完全一致,尤其是多个过滤器叠加时,执行顺序可能与旧版不同。如果你原来的业务逻辑强依赖多个 filter 之间的执行顺序,迁移后一定要靠测试用例锁定行为。

5.4 自定义序列化器的迁移

自定义序列化器在 Fastjson1 中通过实现ObjectSerializer接口完成,Fastjson2 对应的是com.alibaba.fastjson2.writer.ObjectWriter。代码需要做部分调整。

Fastjson1 的写法:

public class UserSerializer implements ObjectSerializer { @Override public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException { // ... } }

Fastjson2 的写法:

public class UserWriter implements ObjectWriter { @Override public void write(JSONWriter jsonWriter, Object object, Object fieldName, Type fieldType, long features) { // ... } }

可以看到,核心方法签名有变化,JSONWriter的操作方式和JSONSerializer也有差异。这种自定义序列化器如果不多,建议直接重写;如果项目里大量依赖自定义序列化器,那就要把这个工作量提前评估进排期里。

5.5 枚举序列化的变化

枚举序列化在 Fastjson1 和 Fastjson2 中默认行为存在细微差异。Fastjson1 默认将枚举序列化为枚举 name,Fastjson2 也是。但如果枚举类里有额外字段,并且你想序列化那个字段,需要注意配置方式。

举例说明,Fastjson1 中可能通过@JSONField标注在枚举常量上让某个字段参与序列化。Fastjson2 中同样支持,但推荐的方式更加明确:实现Enum的序列化逻辑可以用ObjectWriter配合注解处理。从实测结果看,Fastjson2 的枚举序列化在处理复杂枚举(带构造参数、带字段)时反而更稳定,不太容易出现 Fastjson1 那种“枚举序列化后反序列化不回来”的情况。

6. 兼容性坑点排查实录:从编译报错到运行时异常的一线经历

6.1 编译期报错:找不到符号 / 包不存在

迁移最直接的反应就是编译报错。绝大多是 import 路径错误,按第 2 节的对照表改掉就行。如果遇到“找不到符号”的情况,优先检查这个类在 Fastjson2 中是否被改名了或者移动包了。

我遇到的一个具体案例是JSON.toJSONStringWithDateFormat方法。Fastjson1 中有这个便捷方法,Fastjson2 中依然保留,但如果你用了JSON.toJSONStringWithDateFormat(obj, "yyyy-MM-dd", new SerializerFeature[0])这种带数组参数的重载,Fastjson2 中的签名改成了接受JSONWriter.Feature...可变参数,适配起来会稍有变化。

遇到编译错误,最有效的办法是直接翻源码或官方文档,不要凭记忆猜 API。Fastjson2 虽然在类结构上做了兼容,但方法的参数类型、返回类型变化还是有的。

6.2 运行期异常:序列化/反序列化结果不对

编译通过不等于万事大吉,运行期结果差异才是迁移中最容易出问题的地方。这里记录几个我实际踩到的坑。

坑一:日期格式变化

Fastjson1 对java.util.Date默认序列化输出是时间戳,Fastjson2 也是。但如果在字段上配置了@JSONField(format="yyyy-MM-dd HH:mm:ss"),Fastjson2 在某些版本中会严格校验格式字符串,如果你传入了非法格式,启动时不会报错,但运行时会抛异常。相比之下 Fastjson1 会安静地容忍错误格式。迁移后建议对日期字段做一次全量测试,确认格式输出符合预期。

坑二:null 值处理不一致

前面提到过,Fastjson2 中 null 值输出和 null 字符串处理的控制是分场景的。如果一个 bean 里某个字段是String类型且值为 null,你希望序列化出来是"",Fastjson1 需要加WriteNullStringAsEmpty。Fastjson2 还要注意顺序问题:WriteNullsWriteNullStringAsEmpty同时配置时,后者会作用于前者输出的 null 值,把它替换成空字符串。如果你的配置顺序错了,可能输出还是 null。

坑三:泛型反序列化时类型丢失

这个现象在 Fastjson1 的某些版本中偶发,Fastjson2 基本修复了。但部分老代码为了规避 Fastjson1 的 bug,在泛型反序列化后手动做了一次类型转换兜底。比如从JSON.parseObject(json, new TypeReference<List<User>>() {})拿到 List,然后遍历的时候强转 User。这类代码在 Fastjson2 中如果泛型识别正确,强转没问题;如果泛型识别仍然失败(比如 TypeReference 使用不当),抛出来的就是 ClassCastException 而不是之前那种静默情况。

这种问题排查起来比较隐蔽,建议在迁移测试中专门设计泛型嵌套场景,确认反序列化后的实际类型。

坑四:循环引用处理

Fastjson1 有SerializerFeature.DisableCircularReferenceDetect来关闭循环引用检测,Fastjson2 中对应的是JSONWriter.Feature.DisableReferenceDetect。如果你原来的代码里通过全局配置关闭了循环引用检测,迁移后必须同步修改到新枚举上。否则,遇到对象互相引用的场景,Fastjson2 默认会生成{"$ref":"$.xxx"}这类引用标记,而不是输出完整对象,业务上可能产生意料之外的 JSON 结构。

6.3 运行时异常:ClassCastException、JSONException

ClassCastException 常见于泛型场景,在前面已经说过。JSONException 则可能出现在特殊字符、格式错误、AutoType 白名单配置这几个环节。

发现 JSONException 时,不要只盯着报错信息那一段,建议把原始 JSON 字符串截取前后各 100 个字符打印出来。很多时候问题出在字符串中间某个不可见控制字符上。我在一次迁移中就碰到过:上游系统在某个字段的 value 里塞了一个\u0001控制字符,Fastjson1 解析时默认忽略,Fastjson2 直接抛异常。最后排查出数据源问题后,对接方清洗了数据,问题才彻底解决。

7. Spring Boot 项目集成 Fastjson2 的推荐姿势与踩坑记录

7.1 替换 HttpMessageConverter

如果你的 Spring Boot 项目原本是用 Fastjson1 的FastJsonHttpMessageConverter来做 JSON 序列化的,迁移后换成 Fastjson2 对应的FastJsonHttpMessageConverter。Fastjson2 提供了独立的扩展模块:

<dependency> <groupId>com.alibaba.fastjson2</groupId> <artifactId>fastjson2-extension-spring6</artifactId> <version>2.0.53</version> </dependency>

注意 Spring Boot 版本不同,要选对应的 extension 模块。Spring Boot 2.x 用fastjson2-extension-spring5,Spring Boot 3.x 用fastjson2-extension-spring6。这个区分很重要,选错依赖会导致运行期报 NoSuchMethodError。

7.2 MappingJackson2HttpMessageConverter 与 Fastjson2 混用的问题

不少项目里同时存在 Jackson 和 Fastjson 两套 JSON 处理工具,平时相安无事,但迁移 Fastjson2 时会出现一个很隐蔽的问题:Spring MVC 默认的消息转换器是 Jackson 的MappingJackson2HttpMessageConverter,如果你只替换了依赖而没有把自定义的 Fastjson2 转换器注册到HttpMessageConverters里,Controller 返回的对象仍然会走 Jackson 序列化。这时你发现“改了 Fastjson2 但接口返回的 JSON 没变化”,其实是因为请求根本没走到 Fastjson2。

正确的注册方式:

@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); converter.setDefaultCharset(StandardCharsets.UTF_8); converters.add(0, converter); } }

这里把自定义转换器放在列表第一位,确保优先使用 Fastjson2。

7.3 Spring Boot 中日期格式全局配置

Fastjson1 时代,很多人习惯在 Spring Boot 配置文件里设置全局日期格式,比如:

spring.jackson.date-format=yyyy-MM-dd HH:mm:ss

这个配置在 Fastjson2 中不会自动生效,因为 Fastjson2 不是 Jackson 体系。如果你希望 Fastjson2 输出的日期字段统一为yyyy-MM-dd HH:mm:ss,需要在自定义的 FastJsonHttpMessageConverter 里设置:

FastJsonConfig config = new FastJsonConfig(); config.setDateFormat("yyyy-MM-dd HH:mm:ss"); converter.setFastJsonConfig(config);

或者继续用 @JSONField(format = "yyyy-MM-dd HH:mm:ss") 在字段上单独指定。

7.4 拦截器与过滤器中的 JSON 工具类替换

有些项目习惯在拦截器(Interceptor)或过滤器(Filter)里用JSON.parseObject来解析请求体。这些地方如果只改了 import,没有同步检查JSONReader.Feature的配置,可能在处理特定请求时出现 behavior 不一致。建议迁移时把工具类中的 JSON 方法调用统一收敛到一个内部类或 Service 中,这样改起来更集中,后续排查也方便。

8. 高版本特性再认识:Fastjson2 带来的额外红利与使用建议

很多开发者对 Fastjson2 的认知还停留在“性能更好、更安全”,但经过这两年的版本迭代,Fastjson2 在功能和易用性上也有不少值得关注的新特性,实际迁移时不妨一并利用起来。

8.1 JSONPath 表达式能力增强

Fastjson2 对 JSONPath 的支持比 Fastjson1 更完整。比如从一个嵌套很深的 JSON 中快速提取目标值:

String jsonStr = "{\"store\":{\"book\":[{\"title\":\"A\",\"price\":10},{\"title\":\"B\",\"price\":20}]}}"; Object title = JSONPath.extract(jsonStr, "$.store.book[0].title"); System.out.println(title); // A

这在测试接口返回、校验大 JSON 响应时非常方便。旧版本中 JSONPath 在复杂表达式下会有解析慢或失败的问题,Fastjson2 在这块的稳定性好了不少。

8.2 JSONB 二进制序列化

Fastjson2 引入了 JSONB 二进制序列化格式,专门为性能敏感场景设计。同样是序列化一个对象,JSONB 的字节数比文本 JSON 更小、解析更快。如果项目里有大量内部服务间 RPC 通信,且不在乎二进制格式的可读性,JSONB 是一个值得尝试的优化点。

基本用法:

byte[] bytes = JSONB.toBytes(user); User user2 = JSONB.parseObject(bytes, User.class);

不过需要注意,JSONB 格式与文本 JSON 不互通,跨语言调用时得确保对端也能处理 JSONB,所以一般建议先在 Java 服务内部试用,不要一开始就用在对外开放的 API 上。

8.3 数据流处理与 Lambda 支持

Fastjson2 还提供了JSON.parseObject(InputStream)这类流式解析方法,适合处理大文件、大响应体,避免一次性把大量数据加载进内存。如果你有对大 JSON 文件做处理的场景,这个特性比先读成 String 再解析要省内存得多。

8.4 新版 API 与旧版的取舍建议

迁移时不要守旧,把代码里那些为了绕过 Fastjson1 缺陷而写的 workaround 一并清理掉。我见过很多项目里有一堆奇怪的代码,比如反序列化后手动把 JSONObject 转成 Bean、用 JsonPath 后再手动递归取值等。Fastjson2 本身能力更强,该删的 workaround 就删掉,否则代码复杂度一直降不下来,后面维护越来越痛苦。

9. 迁移回归测试清单:照着跑一遍,心里才有底

迁移的最后一环是回归测试。Fastjson2 的兼容性整体不错,但覆盖不全的测试很可能把隐藏问题留给线上。以下是我在项目中实际会跑的一套回归清单,你可以直接拿去用,根据项目情况增删。

  • 基础对象序列化:单层 bean、多层嵌套 bean、包含 List/Map 字段的 bean
  • 日期类型:Date、LocalDate、LocalDateTime、带 @JSONField(format) 的日期字段
  • 枚举序列化:简单枚举、带字段和构造方法的复杂枚举
  • null 值处理:null 字段是否输出、输出空字符串、Map 中的 null 值
  • 泛型反序列化:List<User>Map<String, User>Result<List<User>>三层嵌套泛型
  • 循环引用:对象相互引用时的序列化输出是否符合预期
  • 特殊字符:字符串中包含控制字符、Unicode 转义、HTML 特殊字符的情况
  • 自定义序列化器:实现 ObjectWriter 后的字段输出是否符合预期
  • Filter 链:多个 ValueFilter / NameFilter 叠加时的执行顺序与结果
  • AutoType 场景:开启白名单后的多态反序列化

这 10 类场景基本覆盖了绝大多数业务的 JSON 处理需求。跑完以后,再用 diff 工具把迁移前后的 JSON 输出做对比,凡是出现差异的地方,逐项标记并判断是行为变化还是新 bug。

如果项目是慢慢迁移,建议采用先旁路验证的方式。新代码用 Fastjson2,老代码继续跑 Fastjson1,通过灰度对比线上数据校验一致性,等确认无误后再完全切换。别想着一次切换全量代码,风险太高,出了问题回滚也麻烦。

10. 写在最后:迁移过程里我学到的最重要一件事

代码迁移这件事,最怕的不是技术难点,而是对旧行为的“惯性依赖”。很多兼容性问题不是 Fastjson2 不支持你的写法,而是你过去用 Fastjson1 用出了官方设计之外的“民间用法”。比如依赖 parse 的容错、依赖某些不算规范的 getter 命名、依赖多个 filter 的执行顺序等等。这些用法在 Fastjson1 里确实能跑,但严格来说并不稳妥,Fastjson2 只是把不合法、不合理的行为纠正过来了而已。

所以迁移的时候,心态上不能只想着“让新版本适配我的老代码”,而是同时想一想“我的老代码是否在用正确姿势调 JSON 库”。有些坑能绕就绕,有些坑该改代码就得改代码,前者让你省时间,后者让你以后少踩更多坑。

在这一轮迁移实践中,我最推崇的做法是:分模块、分批、可回滚。先把核心公共模块切换到 Fastjson2,跑一轮单元测试和基础接口验证;再逐步把业务模块切过去,每切一个模块就安排一轮针对性回归。整个过程不要为了追求快而省略测试,上线之后出兼容性问题,排查成本会远高于提前做一轮完整回归的代价。

最后再分享一个小技巧:Fastjson2 官方的fastjson2-extension系列模块更新非常频繁,记得升级依赖时顺手去 GitHub 看 release notes。很多坑其实官方早在 changelog 里写了,只是没逐条翻译成中文。花十分钟读 release notes,能帮你少踩一晚上的坑。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/21 2:36:15

基于OpenCV和Python的车牌识别系统实现:从图像处理到模板匹配

简介&#xff1a;一套基于OpenCV与Python的车牌识别毕业设计项目&#xff0c;整合Tkinter图形界面与SVM分类模型&#xff0c;面向计算机视觉、图像处理方向的本科毕设、课程设计及实战学习者。系统实现车牌定位、字符分割、特征提取与自动识别&#xff0c;代码覆盖灰度化、直方…

作者头像 李华
网站建设 2026/9/21 2:35:23

连续小波变换原理详解:从傅里叶死穴到Python时频图实操

站在信号处理这个行当里摸爬滚打这些年&#xff0c;我越来越觉得“连续小波变换&#xff08;CWT&#xff09;”是个被低估的工具。很多人一听“时频局部分析”就觉得高深&#xff0c;其实它解决的是一个特别接地气的问题&#xff1a;傅里叶变换能告诉你信号里有什么频率&#x…

作者头像 李华
网站建设 2026/9/21 2:32:42

百货零售数字化转型方案拆解:从战略到落地

简介&#xff1a;德勤大型百货零售集团数字化转型解决方案以八十五页演示文稿形式呈现&#xff0c;面向零售企业中高层、数字化转型项目团队及咨询从业者。方案深入分析了零售业从传统模式向数字化、智能化转型的关键时期&#xff0c;覆盖云计算、大数据、物联网等新兴技术带来…

作者头像 李华
网站建设 2026/9/21 2:31:34

torch2trt源码深度解析:从PyTorch到TensorRT的企业级落地指南

先说明一句&#xff1a;这篇文章会以企业技术尽调的口吻来写&#xff0c;所有分析都基于源码实证&#xff0c;不掺水分。torch2trt 这个项目在 PyTorch 转 TensorRT 的生态里名气很大&#xff0c;但真正打开源码逐行读过的团队其实不多。很多同学只是 pip install 之后跑通了 d…

作者头像 李华