1. 项目概述为什么我们需要关注FastJson的全局配置如果你在Java项目里用过JSON序列化和反序列化那FastJson这个名字你肯定不陌生。作为阿里开源的高性能JSON处理库它以速度快、功能全著称几乎成了国内Java开发者的标配。但用的人多踩的坑也多。很多开发者包括我自己刚开始用FastJson的时候都是直接JSON.parseObject()和JSON.toJSONString()一把梭直到在线上遇到了各种奇奇怪怪的问题日期格式不对、空值被过滤了、循环引用导致栈溢出、甚至因为一个未知字段就抛异常中断了整个流程。这些问题往往不是FastJson的Bug而是我们没有正确地配置它。FastJson提供了非常丰富的特性但很多特性的默认行为可能并不符合我们特定业务场景下的预期。比如默认情况下FastJson在序列化时会忽略值为null的字段但你的前端可能恰恰需要这个null值来区分“字段不存在”和“字段值为空”这两种状态。再比如默认的日期格式是yyyy-MM-dd HH:mm:ss但你的老系统接口可能要求毫秒时间戳。“全局配置”就是为了解决这些问题而生的。它意味着你不需要在每个toJSONString()或parseObject()的地方都写一堆参数而是通过一次性的设置让整个应用内的FastJson行为都符合你的规范。这不仅能提升开发效率不用到处复制粘贴配置代码更能保证行为的一致性减少因配置疏忽导致的线上事故。最近FastJson更新到了1.2.84版本修复了一些安全问题同时也意味着一些默认行为或配置方式可能有微调现在正是重新梳理和巩固全局配置知识的好时机。2. 全局配置的核心入口与设计哲学FastJson的全局配置其核心是围绕一个叫做ParserConfig和SerializeConfig的静态单例以及一个全局的JSON.DEFAULT_GENERATE_FEATURE和JSON.DEFAULT_PARSER_FEATURE来展开的。理解这个设计哲学很重要FastJson将配置分为“序列化配置”和“反序列化配置”两大块并且提供了全局Global和实例Instance两个级别的控制粒度。SerializeConfig.globalInstance 这个对象掌管着全局的序列化行为。比如当你要把某个Java对象转换成JSON字符串时用哪个序列化器ObjectSerializer来处理它就是由这里决定的。你可以在这里为特定的Class注册自定义的序列化器改变默认的序列化逻辑。ParserConfig.globalInstance 这是反序列化的全局大脑。它负责在将JSON字符串解析成Java对象时决定如何创建实例、如何查找构造函数、以及如何处理“自动类型匹配”AutoType这个高危特性。FastJson的许多反序列化漏洞都与AutoType有关因此全局配置在这里首先需要考虑的是安全。JSON.DEFAULT_GENERATE_FEATURE和JSON.DEFAULT_PARSER_FEATURE 这是两个静态的int类型变量通过位运算来存储一系列的“特性开关”。序列化特性SerializerFeature控制着输出JSON的格式比如是否格式化输出、是否输出null值等。反序列化特性Feature控制着解析JSON时的行为比如是否允许注释、是否允许单引号等。设置这两个全局变量就相当于为所有不显式指定特性的操作设置了默认值。这种设计的优点是灵活。你既可以通过修改全局配置来统一行为也可以在某个特定的toJSONString或parseObject调用时通过传入SerializerFeature或Feature参数来覆盖全局设置实现特殊处理。2.1 配置的优先级与生效范围搞清楚配置的优先级能帮你避免“为什么我配了却没生效”的困惑。FastJson的配置生效遵循一个从特殊到一般的原则方法参数最高在调用JSON.toJSONString(object, SerializerFeature... features)时直接传入的features参数优先级最高。它会覆盖全局和实例级别的任何相同特性的设置。实例配置次之你可以创建JSONSerializer或DefaultJSONParser的实例并为其单独设置config和features。这个实例的配置只对本次操作生效优先级高于全局配置。全局配置兜底当上面两级都没有明确配置时就使用SerializeConfig.globalInstance、ParserConfig.globalInstance以及JSON.DEFAULT_GENERATE_FEATURE等全局静态变量。所以当你进行全局配置时心里要清楚你是在设定整个应用的“默认值”和“安全基线”。任何局部的、特殊的操作都可以覆盖这个基线但全局配置确保了在大多数情况下你的应用能以一种安全、统一的方式运行。3. 序列化全局配置详解与爬坑实践序列化配置的目标是控制Java对象如何变成JSON字符串。我们主要从特性开关SerializerFeature和序列化器注册两方面入手。3.1 关键SerializerFeature特性解析与全局设置SerializerFeature是一个枚举每个枚举值代表一个特性开关。全局设置通过修改JSON.DEFAULT_GENERATE_FEATURE这个静态变量来实现。通常我们会在应用启动时如Spring Boot的PostConstruct或一个静态代码块中进行设置。import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.serializer.SerializerFeature; public class FastJsonGlobalConfig { static { // 1. 先清除所有默认特性可选通常我们是在默认基础上增删 // int defaultFeatures JSON.DEFAULT_GENERATE_FEATURE; // 2. 设置我们想要的全局特性组合 // 常用组合输出Null值禁用循环引用检测日期用时间戳 JSON.DEFAULT_GENERATE_FEATURE SerializerFeature.config( JSON.DEFAULT_GENERATE_FEATURE, // 在现有默认值基础上修改 SerializerFeature.WriteMapNullValue, // 关键输出Map中值为Null的字段 SerializerFeature.WriteNullListAsEmpty, // 将Null的List输出为[] SerializerFeature.WriteNullStringAsEmpty, // 将Null的String输出为 SerializerFeature.WriteNullBooleanAsFalse, // 将Null的Boolean输出为false SerializerFeature.WriteDateUseDateFormat, // 使用日期格式需配合全局日期格式 SerializerFeature.DisableCircularReferenceDetect // 禁用循环引用检测提升性能 ); // 3. 移除我们不想要的特性 JSON.DEFAULT_GENERATE_FEATURE SerializerFeature.config( JSON.DEFAULT_GENERATE_FEATURE, SerializerFeature.PrettyFormat, // 移除默认不格式化美化输出节省流量 SerializerFeature.WriteClassName, // 移除绝对不要全局输出type信息有安全风险 false // false表示移除这些特性 ); // 4. 设置全局日期格式 JSON.DEFFAULT_DATE_FORMAT yyyy-MM-dd HH:mm:ss.SSS; // 精确到毫秒 } }关键特性爬坑指南WriteMapNullValue这是最常被问到的配置。FastJson默认会忽略值为null的字段。如果你的API要求明确返回null必须开启此特性。坑点开启了它Map里的null值会输出但普通Java对象字段的null值输出还需要在字段上或类上使用JSONField(serialzeFeatures {SerializerFeature.WriteMapNullValue})注解配合。全局配置对此注解标注的字段生效。DisableCircularReferenceDetect循环引用检测默认开启会在对象间存在相互引用时通过输出$ref引用来避免栈溢出。但在高性能场景下这个检测有开销。建议如果你能确保你的业务模型没有循环引用比如纯VO/DTO全局禁用它可提升性能。如果存在循环引用禁用会导致栈溢出错误。WriteClassName高危绝对不要全局开启它会在JSON中输出type字段记录对象的全类名。这是FastJson AutoType功能的基础也是反序列化漏洞的根源。除非在极度可控的内部通讯场景否则永远不要开启。日期格式通过JSON.DEFFAULT_DATE_FORMAT设置。注意如果同时开启了SerializerFeature.WriteDateUseTimestamp则会优先输出时间戳格式设置失效。两者通常二选一。3.2 自定义序列化器ObjectSerializer全局注册有时候默认的序列化逻辑不满足需求。比如你想把枚举类型序列化成其code值而非name或者想对某个复杂对象进行特殊的格式化处理。import com.alibaba.fastjson.serializer.ObjectSerializer; import com.alibaba.fastjson.serializer.JSONSerializer; import com.alibaba.fastjson.serializer.SerializeWriter; import com.alibaba.fastjson.serializer.SerializeConfig; import java.io.IOException; import java.lang.reflect.Type; public class FastJsonGlobalConfig { public static void registerCustomSerializers() { SerializeConfig globalSerializeConfig SerializeConfig.globalInstance; // 示例1为自定义的Money类型注册序列化器 globalSerializeConfig.put(Money.class, new ObjectSerializer() { Override public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException { if (object null) { serializer.writeNull(); return; } Money money (Money) object; // 将Money对象序列化为 100.00 CNY 格式的字符串 String value String.format(%.2f %s, money.getAmount(), money.getCurrency()); serializer.write(value); } }); // 示例2为某个第三方库的类如JodaTime的DateTime注册序列化器 // 如果项目已经引入了fastjson的扩展包如 fastjson-extension可能已有内置支持。 // 否则需要自己注册。 // globalSerializeConfig.put(org.joda.time.DateTime.class, new JodaDateTimeSerializer()); } }爬坑点自定义序列化器是强依赖类型的。确保你注册的Class对象是精确的类型。对于泛型集合如ListString序列化器是针对String类型而不是List。另外注册自定义序列化器后其优先级高于FastJson内置的默认序列化逻辑。4. 反序列化全局配置详解与安全加固反序列化配置是重中之重直接关系到应用的安全。核心是ParserConfig。4.1 关键Feature特性解析与全局设置反序列化的Feature控制了解析JSON字符串时的宽松程度。同样通过静态变量设置。import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.Feature; public class FastJsonGlobalConfig { static { // 设置全局反序列化特性 JSON.DEFAULT_PARSER_FEATURE Feature.config( JSON.DEFAULT_PARSER_FEATURE, Feature.SupportAutoType, // **谨慎开启见下方安全说明** Feature.AllowComment, // 允许JSON中存在//或/* */注释 Feature.AllowSingleQuotes, // 允许使用单引号作为字符串引号 Feature.AllowUnQuotedFieldNames, // 允许字段名不用引号包裹非标准JSON Feature.IgnoreNotMatch, // 忽略JSON中存在的、但Java对象不存在的字段静默忽略 Feature.UseBigDecimal // 将JSON数字反序列化为BigDecimal而非Double避免精度丢失 ); // 移除一些可能不安全的特性 JSON.DEFAULT_PARSER_FEATURE Feature.config( JSON.DEFAULT_PARSER_FEATURE, Feature.SupportNonPublicField, // 移除不支持反序列化非public字段更安全 false ); } }安全爬坑指南SupportAutoType这是FastJson安全问题的风暴眼。开启后FastJson会根据JSON中的type信息来实例化任意类。攻击者可以构造恶意JSON导致远程代码执行RCE。强烈建议在任何生产环境中全局关闭此特性即从默认特性中移除。如果业务必须使用必须使用白名单机制见下文。IgnoreNotMatch建议开启。默认情况下如果JSON中有Java对象不存在的字段FastJson会抛出异常。开启此特性后这些多余字段会被静默忽略提高了接口的兼容性和健壮性避免因为前端多传了一个字段而导致整个解析失败。UseBigDecimal对于金融、金额相关字段务必开启。JSON中的数字默认会被反序列化为Double可能存在精度损失。开启此特性后所有数字都会变成BigDecimal但要注意接收字段的类型也需是BigDecimal。4.2 AutoType白名单配置安全生命线这是FastJson全局配置中最关键的安全措施。即使你全局关闭了SupportAutoType在某些版本或通过其他方式攻击链仍可能被触发。因此设置严格的白名单是必须的。import com.alibaba.fastjson.parser.ParserConfig; public class FastJsonGlobalConfig { public static void initAutoTypeSafe() { ParserConfig globalParserConfig ParserConfig.getGlobalInstance(); // 1. 首先全局关闭AutoType支持最安全 globalParserConfig.setAutoTypeSupport(false); // 2. 如果确有业务需要如RPC框架、消息队列传输复杂对象使用白名单 // 添加允许自动反序列化的类到白名单。支持前缀匹配和完整类名。 globalParserConfig.addAccept(com.yourcompany.dto.); globalParserConfig.addAccept(com.yourcompany.model.); globalParserConfig.addAccept(java.util.HashMap); globalParserConfig.addAccept(java.util.ArrayList); // 3. 黑名单FastJson内部维护一般不需要自己添加 // globalParserConfig.addDeny(com.危险.类); // 4. 【重要】在1.2.68及以上版本推荐使用safeMode这是最严格的模式 // 开启safeMode后白名单机制也会失效任何AutoType功能都不再可用。 // ParserConfig.getGlobalInstance().setSafeMode(true); } }版本差异与爬坑1.2.68以下版本主要依靠setAutoTypeSupport(false)和白名单。但历史上存在白名单被绕过的漏洞。1.2.68及以上版本引入了safeMode。对于安全要求极高的场景强烈建议直接启用setSafeMode(true)。一旦启用所有基于type的自动类型推断功能都将失效从根本上杜绝了相关漏洞。你需要评估业务是否真的需要AutoType很多场景下其实并不需要。4.3 自定义反序列化器与特定字段处理和序列化类似你也可以为特定类型注册自定义的反序列化器ObjectDeserializer用于处理特殊的解析逻辑。import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer; import com.alibaba.fastjson.parser.DefaultJSONParser; import com.alibaba.fastjson.parser.ParserConfig; import java.lang.reflect.Type; public class FastJsonGlobalConfig { public static void registerCustomDeserializers() { ParserConfig globalParserConfig ParserConfig.getGlobalInstance(); // 示例将字符串 100.00 CNY 反序列化为Money对象 globalParserConfig.putDeserializer(Money.class, new ObjectDeserializer() { Override public T T deserialze(DefaultJSONParser parser, Type type, Object fieldName) { String text parser.parseObject(String.class); // 先解析成字符串 if (text null || text.isEmpty()) { return null; } String[] parts text.split( ); Money money new Money(); money.setAmount(new BigDecimal(parts[0])); money.setCurrency(parts[1]); return (T) money; } Override public int getFastMatchToken() { return 0; // 匹配字符串类型的token } }); } }5. 常见问题排查与实战技巧实录即使配置好了在实际开发中还是会遇到各种问题。这里记录几个我踩过的坑和解决方案。5.1 配置不生效检查配置时机和依赖冲突问题现象在static块或PostConstruct方法里配置了全局特性但实际运行时发现没生效。排查思路时机问题确保你的配置代码在任何FastJson API被调用之前执行。如果其他依赖库比如某个Spring Boot Starter在Spring容器初始化早期就使用了FastJson你的配置可能就晚了。最稳妥的方式是使用Spring的ApplicationListenerContextRefreshedEvent或PostConstruct在一个配置类中并确保这个配置类被优先加载高Order值。依赖冲突使用mvn dependency:tree或Gradle的依赖树命令检查项目中是否存在多个不同版本的FastJson Jar包。低版本的Jar可能覆盖了你的全局配置类。确保依赖统一。局部覆盖检查代码中是否有地方在调用FastJson方法时显式传入了SerializerFeature或Feature参数这些局部参数会覆盖全局设置。5.2 日期序列化/反序列化混乱问题现象前端传一个时间戳后端反序列化后日期不对或者后端返回的日期格式前端解析不了。解决方案明确格式全局设置JSON.DEFFAULT_DATE_FORMAT并确保序列化和反序列化使用同一种格式。如果接口是时间戳就全局开启SerializerFeature.WriteDateUseTimestamp并关闭日期格式。使用注解在具体的日期字段上使用JSONField(format yyyy-MM-dd)其优先级高于全局设置。时区问题FastJson默认使用JVM的默认时区。如果你的服务器时区是UTC而业务时间是东八区就会差8小时。可以在全局配置中指定时区但FastJson全局配置没有直接设置时区的方法。通常的解决方法是在序列化时使用JSON.toJSONStringWithDateFormat(object, “yyyy-MM-dd HH:mm:ss”, TimeZone.getTimeZone(“GMT8”))。或者在Spring Boot中配置全局的Jackson如果你也用时区并确保业务代码不混用两种JSON库。5.3 泛型对象反序列化丢失类型信息问题现象解析类似{data: [{id:1}]}的JSON到ResultListUser对象时data字段被反序列化成ListMap而不是ListUser。原因Java的泛型擦除机制导致运行时无法获取ListUser中的User类型。解决方案使用TypeReference这是最推荐的方式。String json ...; ResultListUser result JSON.parseObject(json, new TypeReferenceResultListUser(){});在类中保留类型信息如果你的Result类是自定义的可以使用JSONField(deserializeUsing MyDeserializer.class)指定自定义反序列化器在反序列化器中硬编码类型信息。传递Type参数JSON.parseObject(String text, Type type)。5.4 性能调优相关配置关闭循环引用检测如前述如果确定无循环引用全局设置SerializerFeature.DisableCircularReferenceDetect。使用SerializeConfig和ParserConfig的单例避免频繁创建新的config实例。对于超大JSON考虑使用JSONReader和JSONWriter进行流式解析和生成而不是一次性将整个JSON读入内存。关注ParserConfig的缓存ParserConfig会缓存已解析类的反序列化器。默认大小是1024。在极端动态类加载的场景下注意监控。5.5 升级到1.2.83/1.2.84的注意事项最近版本主要修复安全漏洞。升级时阅读官方Release Notes了解行为变更。例如某些版本可能调整了默认启用的Feature。全面测试用你的业务JSON用例进行充分测试确保反序列化行为符合预期。重申安全配置再次检查AutoTypeSupport是否关闭白名单是否设置强烈考虑启用safeMode。依赖传递确保所有子模块的FastJson版本都统一升级避免混用。全局配置不是一劳永逸的它需要根据你的项目架构、安全要求和业务特点进行仔细设计和测试。最好的实践是在一个独立的配置类中完成所有FastJson的全局设置并在项目文档中明确说明这些设置的原因和影响这样无论团队中的谁接手项目都能快速理解并维护这套JSON处理规范。