SpringBoot项目从fastjson1.x升级到fastjson2.x,Redis序列化配置怎么改?(附完整代码)

SpringBoot项目从fastjson1.x升级到fastjson2.x,Redis序列化配置怎么改?(附完整代码) SpringBoot项目从fastjson1.x升级到fastjson2.x的Redis序列化全攻略当你决定将SpringBoot项目中的fastjson从1.x版本升级到2.x版本时Redis序列化配置的调整是一个不可忽视的关键环节。许多开发者在升级过程中发现原本运行良好的FastJsonRedisSerializer突然失效导致项目启动失败或数据序列化异常。本文将带你深入理解fastjson2的API变化并提供一套完整的解决方案。1. 为什么需要升级fastjsonfastjson作为阿里巴巴开源的高性能JSON处理库在Java生态中广泛应用。fastjson2相较于1.x版本在性能、安全性和API设计上都有显著改进性能提升fastjson2的序列化/反序列化速度比1.x版本提升约30%内存优化减少了约20%的内存占用安全性增强修复了1.x版本中的多个安全漏洞API简化移除了部分冗余API使代码更加简洁然而这些改进也带来了兼容性问题特别是在Redis序列化场景下// fastjson1.x的序列化方式已废弃 JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET); // fastjson2.x的新序列化方式 JSON.toJSONBytes(t);2. 新旧API对比与问题诊断2.1 主要API变化fastjson2对核心API进行了重构以下是几个关键变化点功能点fastjson1.x APIfastjson2.x API序列化JSON.toJSONString()JSON.toJSONBytes()反序列化JSON.parseObject()JSON.parseObject()类型支持ParserConfig.setAutoTypeSupport()自动类型支持默认开启特性配置SerializerFeature枚举JSONWriter.Feature2.2 常见升级问题序列化格式不兼容fastjson2默认使用新的二进制格式可能导致旧数据无法正确反序列化SerializerFeature失效许多1.x版本的特性在2.x中已被移除或改名自动类型支持变化fastjson2修改了autoType的工作机制提示升级前务必对现有Redis中的数据进行备份以防升级过程中数据丢失。3. 自定义FastJson2JsonRedisSerializer实现下面是一个完整的FastJson2JsonRedisSerializer实现兼容fastjson2并整合了Jackson的ObjectMapperimport com.alibaba.fastjson2.JSON; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.TypeFactory; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.SerializationException; import org.springframework.util.Assert; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; public class FastJson2JsonRedisSerializerT implements RedisSerializerT { private ObjectMapper objectMapper new ObjectMapper(); public static final Charset DEFAULT_CHARSET StandardCharsets.UTF_8; private ClassT clazz; public FastJson2JsonRedisSerializer(ClassT clazz) { super(); this.clazz clazz; } Override public byte[] serialize(T t) throws SerializationException { if (t null) { return new byte[0]; } return JSON.toJSONBytes(t); } Override public T deserialize(byte[] bytes) throws SerializationException { if (bytes null || bytes.length 0) { return null; } String str new String(bytes, DEFAULT_CHARSET); return JSON.parseObject(str, clazz); } public void setObjectMapper(ObjectMapper objectMapper) { Assert.notNull(objectMapper, objectMapper must not be null); this.objectMapper objectMapper; } protected JavaType getJavaType(Class? clazz) { return TypeFactory.defaultInstance().constructType(clazz); } }关键改进点移除了fastjson1.x特有的SerializerFeature和ParserConfig使用JSON.toJSONBytes()替代JSON.toJSONString()保留了与JacksonObjectMapper的兼容性4. Redis配置类完整实现下面是更新后的Redis配置类整合了自定义序列化器import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; Configuration EnableCaching public class RedisConfig extends CachingConfigurerSupport { Bean SuppressWarnings(value {unchecked, rawtypes}) public RedisTemplateObject, Object redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplateObject, Object template new RedisTemplate(); template.setConnectionFactory(connectionFactory); FastJson2JsonRedisSerializer serializer new FastJson2JsonRedisSerializer(Object.class); ObjectMapper mapper new ObjectMapper(); mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL); serializer.setObjectMapper(mapper); template.setValueSerializer(serializer); template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(serializer); template.afterPropertiesSet(); return template; } }配置要点说明使用StringRedisSerializer处理所有key的序列化对value使用自定义的FastJson2JsonRedisSerializer配置ObjectMapper以支持多态类型处理确保所有属性都正确初始化后调用afterPropertiesSet()5. 测试与验证策略升级完成后必须进行全面的测试验证5.1 单元测试示例SpringBootTest public class RedisSerializerTest { Autowired private RedisTemplateString, Object redisTemplate; Test public void testBasicSerialization() { User user new User(test, 30); redisTemplate.opsForValue().set(user:1, user); User cachedUser (User) redisTemplate.opsForValue().get(user:1); assertEquals(user.getName(), cachedUser.getName()); } Test public void testComplexObjectSerialization() { MapString, Object complexObject new HashMap(); complexObject.put(list, Arrays.asList(1, 2, 3)); complexObject.put(nested, new User(nested, 25)); redisTemplate.opsForValue().set(complex:1, complexObject); MapString, Object cached (MapString, Object) redisTemplate.opsForValue().get(complex:1); assertNotNull(cached.get(list)); assertEquals(3, ((List)cached.get(list)).size()); } }5.2 兼容性测试要点基本数据类型String、Integer、Long等集合类型List、Set、Map自定义对象包含嵌套结构的POJO特殊字符包含UTF-8特殊字符的字符串null值处理确保能正确处理null值5.3 性能测试建议使用JMH进行基准测试比较升级前后的性能差异BenchmarkMode(Mode.Throughput) OutputTimeUnit(TimeUnit.SECONDS) public class SerializationBenchmark { private static final User testUser new User(benchmark, 99); private static final FastJson2JsonRedisSerializerUser serializer new FastJson2JsonRedisSerializer(User.class); Benchmark public void measureSerialization() { serializer.serialize(testUser); } Benchmark public void measureDeserialization(Blackhole bh) { byte[] data serializer.serialize(testUser); bh.consume(serializer.deserialize(data)); } }6. 高级配置与优化技巧6.1 自定义序列化特性虽然fastjson2移除了SerializerFeature但提供了新的配置方式// 配置序列化特性 JSON.config(JSONWriter.Feature.WriteNulls, JSONReader.Feature.SupportAutoType); // 在序列化器中应用 Override public byte[] serialize(T t) throws SerializationException { if (t null) { return new byte[0]; } JSONWriter.Context context JSONFactory.createWriteContext(); context.config(JSONWriter.Feature.WriteClassName); return JSON.toJSONBytes(t, context); }6.2 处理日期格式fastjson2对日期处理也有变化建议统一配置public class FastJson2JsonRedisSerializerT implements RedisSerializerT { // ... static { JSON.configDateFormat(yyyy-MM-dd HH:mm:ss); } // ... }6.3 大对象处理优化对于大对象的序列化可以使用流式API减少内存占用Override public byte[] serialize(T t) throws SerializationException { if (t null) { return new byte[0]; } try (ByteArrayOutputStream bos new ByteArrayOutputStream(); JSONWriter writer JSONWriter.of(bos)) { writer.writeAny(t); return bos.toByteArray(); } catch (IOException e) { throw new SerializationException(Could not serialize, e); } }6.4 混合序列化策略对于大型系统可以考虑针对不同类型采用不同的序列化策略public class HybridRedisSerializer implements RedisSerializerObject { private final RedisSerializerString stringSerializer new StringRedisSerializer(); private final FastJson2JsonRedisSerializerObject jsonSerializer new FastJson2JsonRedisSerializer(Object.class); Override public byte[] serialize(Object o) throws SerializationException { if (o instanceof String) { return stringSerializer.serialize((String) o); } return jsonSerializer.serialize(o); } Override public Object deserialize(byte[] bytes) throws SerializationException { try { // 尝试作为String反序列化 return stringSerializer.deserialize(bytes); } catch (SerializationException e) { // 如果不是String尝试作为JSON对象反序列化 return jsonSerializer.deserialize(bytes); } } }7. 常见问题解决方案7.1 类型信息丢失问题症状反序列化时得到LinkedHashMap而非原始类型解决方案在ObjectMapper中启用默认类型信息ObjectMapper mapper new ObjectMapper(); mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL); serializer.setObjectMapper(mapper);7.2 循环引用问题症状对象包含循环引用时序列化失败解决方案配置fastjson2处理循环引用JSON.config(JSONWriter.Feature.ReferenceDetection);7.3 兼容旧数据格式症状需要读取fastjson1.x序列化的旧数据解决方案实现兼容性反序列化逻辑Override public T deserialize(byte[] bytes) throws SerializationException { if (bytes null || bytes.length 0) { return null; } String str new String(bytes, DEFAULT_CHARSET); try { // 先尝试fastjson2的方式 return JSON.parseObject(str, clazz); } catch (Exception e) { // 如果失败尝试兼容fastjson1.x的格式 try { return JSON.parseObject(str, clazz, JSONReader.Feature.SupportAutoType); } catch (Exception ex) { throw new SerializationException(Could not deserialize, ex); } } }7.4 性能调优建议重用序列化器实例避免频繁创建序列化器合理配置缓冲区大小对于大对象预设合适的缓冲区选择合适的特性只启用必要的序列化特性监控序列化性能记录关键操作的耗时// 性能优化的序列化器配置 public class OptimizedFastJson2JsonRedisSerializerT implements RedisSerializerT { private static final int INITIAL_BUFFER_SIZE 1024; // 1KB初始缓冲区 private final ClassT clazz; public OptimizedFastJson2JsonRedisSerializer(ClassT clazz) { this.clazz clazz; // 预配置常用特性 JSON.config( JSONWriter.Feature.FieldBased, JSONWriter.Feature.WriteNulls, JSONReader.Feature.SupportAutoType ); } Override public byte[] serialize(T t) throws SerializationException { if (t null) { return new byte[0]; } try (ByteArrayOutputStream bos new ByteArrayOutputStream(INITIAL_BUFFER_SIZE); JSONWriter writer JSONWriter.of(bos)) { writer.writeAny(t); return bos.toByteArray(); } catch (IOException e) { throw new SerializationException(Could not serialize, e); } } // ... 其他方法保持不变 }