拓冰建站拓冰建站
首页 / 资讯中心 / 正文

从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 groupIdcom.alibaba.fastjson2/groupId artifactIdfastjson2/artifactId version2.0.53/version /dependencyGradle 对应写法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 groupIdcom.alibaba.fastjson2/groupId artifactIdfastjson2-extension-spring5/artifactId version2.0.53/version /dependency不要急着把兼容包当成终态方案它只能帮你过渡。长期来看代码里混着新旧两套包名会把团队搞晕后续维护起来非常痛苦。2.2 import 路径的全量替换方案Fastjson2 官方设计了一个非常贴心的机制com.alibaba.fastjson下的核心类在 Fastjson2 中仍然存在但是需要换成com.alibaba.fastjson2包。对于大部分基础用法代码逻辑可以基本保持不变变的只是 import 语句。以最常见的几个类为例对应关系如下旧包名Fastjson 1.x新包名Fastjson2com.alibaba.fastjson.JSONcom.alibaba.fastjson2.JSONcom.alibaba.fastjson.JSONObjectcom.alibaba.fastjson2.JSONObjectcom.alibaba.fastjson.JSONArraycom.alibaba.fastjson2.JSONArraycom.alibaba.fastjson.JSONExceptioncom.alibaba.fastjson2.JSONExceptioncom.alibaba.fastjson.TypeReferencecom.alibaba.fastjson2.TypeReferencecom.alibaba.fastjson.annotation.JSONFieldcom.alibaba.fastjson2.annotation.JSONFieldcom.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 的写法如下MapString, ListUser map JSON.parseObject(jsonStr, new TypeReferenceMapString, ListUser() {});Fastjson2 也支持同样的写法仍然使用TypeReference。但是要注意TypeReference所在的包从com.alibaba.fastjson变成了com.alibaba.fastjson2所以import需要同步更换。我在实际迁移过程中遇到过一个比较隐蔽的差异当泛型嵌套层级较深时Fastjson2 对泛型的解析在某些边界情况下会走不同的解析逻辑。具体来说像ResultPageDataUser这种三层泛型嵌套Fastjson1 偶尔会出现类型丢失问题反序列化出来的列表里面的对象是 JSONObject 而不是 User。Fastjson2 在泛型保留上做得更好基本不会再出现这类问题。但如果你的业务里依赖 Fastjson1 那种“类型丢失但还能打印出对象”的容错这次迁移反而可能暴露出原先被掩盖的类型转换错误。3.3 JSONObject 的 getJSONObject / getJSONArrayJSONObject 的日常操作在 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 对应的值是nullFastjson1 的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方法参数中直接传 FeatureString json JSON.toJSONString(data, JSONWriter.Feature.WriteNulls);也支持在具体对象上配置全局或局部 JSONWriter 上下文。个人建议还是用方法参数的方式直观且局部可控不污染全局设置。序列化过程中如果你的需求是“所有值为 null 的字段也要输出”我在迁移中发现了一个容易踩的细节Fastjson2 有两个看起来很像的枚举——WriteNulls和WriteMapNullValue。在大多数场景下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);换成 Fastjson2JSON.parseObject(jsonStr, User.class, JSONReader.Feature.SupportNonPublicField);Feature 类的迁移路径很清楚但要注意的是某些 Feature 在 Fastjson2 中改名了。举一个典型例子Fastjson1Feature.SupportClassForName在 Fastjson2 中对应JSONReader.Feature.SupportClassForNameFastjson1Feature.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.Feature或JSONReader.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 的deserializeUsing和serializeUsing支持度保持得不错如果你使用了自定义序列化器通常只需要改 import 包名就能正常运行。5.2 JSONType 注解的变化JSONType 在 Fastjson1 中可作为类级别注解用来指定序列化器、反序列化器以及 includes/excludes。Fastjson2 依然支持 JSONType但某些属性的底层处理逻辑有变化。一个我在实践中遇到的差异是JSONType(ignores {...})属性在 Fastjson1 中是忽略指定字段Fastjson2 中也是一样。但如果你在类继承的父类中定义了 JSONTypeFastjson2 对父类注解的继承和识别规则与 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 中依然支持ValueFilter、NameFilter、BeforeFilter这些接口但它们对应的包路径变成了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 默认将枚举序列化为枚举 nameFastjson2 也是。但如果枚举类里有额外字段并且你想序列化那个字段需要注意配置方式。举例说明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(formatyyyy-MM-dd HH:mm:ss)Fastjson2 在某些版本中会严格校验格式字符串如果你传入了非法格式启动时不会报错但运行时会抛异常。相比之下 Fastjson1 会安静地容忍错误格式。迁移后建议对日期字段做一次全量测试确认格式输出符合预期。坑二null 值处理不一致前面提到过Fastjson2 中 null 值输出和 null 字符串处理的控制是分场景的。如果一个 bean 里某个字段是String类型且值为 null你希望序列化出来是Fastjson1 需要加WriteNullStringAsEmpty。Fastjson2 还要注意顺序问题WriteNulls与WriteNullStringAsEmpty同时配置时后者会作用于前者输出的 null 值把它替换成空字符串。如果你的配置顺序错了可能输出还是 null。坑三泛型反序列化时类型丢失这个现象在 Fastjson1 的某些版本中偶发Fastjson2 基本修复了。但部分老代码为了规避 Fastjson1 的 bug在泛型反序列化后手动做了一次类型转换兜底。比如从JSON.parseObject(json, new TypeReferenceListUser() {})拿到 List然后遍历的时候强转 User。这类代码在 Fastjson2 中如果泛型识别正确强转没问题如果泛型识别仍然失败比如 TypeReference 使用不当抛出来的就是 ClassCastException 而不是之前那种静默情况。这种问题排查起来比较隐蔽建议在迁移测试中专门设计泛型嵌套场景确认反序列化后的实际类型。坑四循环引用处理Fastjson1 有SerializerFeature.DisableCircularReferenceDetect来关闭循环引用检测Fastjson2 中对应的是JSONWriter.Feature.DisableReferenceDetect。如果你原来的代码里通过全局配置关闭了循环引用检测迁移后必须同步修改到新枚举上。否则遇到对象互相引用的场景Fastjson2 默认会生成{$ref:$.xxx}这类引用标记而不是输出完整对象业务上可能产生意料之外的 JSON 结构。6.3 运行时异常ClassCastException、JSONExceptionClassCastException 常见于泛型场景在前面已经说过。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 groupIdcom.alibaba.fastjson2/groupId artifactIdfastjson2-extension-spring6/artifactId version2.0.53/version /dependency注意 Spring Boot 版本不同要选对应的 extension 模块。Spring Boot 2.x 用fastjson2-extension-spring5Spring 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(ListHttpMessageConverter? 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-formatyyyy-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 值泛型反序列化ListUser、MapString, User、ResultListUser三层嵌套泛型循环引用对象相互引用时的序列化输出是否符合预期特殊字符字符串中包含控制字符、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能帮你少踩一晚上的坑。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门