当前位置: 首页 > news >正文

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

SpringBoot项目从fastjson1.x升级到fastjson2.x的Redis序列化全攻略

当你决定将SpringBoot项目中的fastjson从1.x版本升级到2.x版本时,Redis序列化配置的调整是一个不可忽视的关键环节。许多开发者在升级过程中发现,原本运行良好的FastJsonRedisSerializer突然失效,导致项目启动失败或数据序列化异常。本文将带你深入理解fastjson2的API变化,并提供一套完整的解决方案。

1. 为什么需要升级fastjson?

fastjson作为阿里巴巴开源的高性能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.Feature

2.2 常见升级问题

  1. 序列化格式不兼容:fastjson2默认使用新的二进制格式,可能导致旧数据无法正确反序列化
  2. SerializerFeature失效:许多1.x版本的特性在2.x中已被移除或改名
  3. 自动类型支持变化:fastjson2修改了autoType的工作机制

提示:升级前务必对现有Redis中的数据进行备份,以防升级过程中数据丢失。

3. 自定义FastJson2JsonRedisSerializer实现

下面是一个完整的FastJson2JsonRedisSerializer实现,兼容fastjson2并整合了Jackson的ObjectMapper

import 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 FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> { private ObjectMapper objectMapper = new ObjectMapper(); public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; private Class<T> clazz; public FastJson2JsonRedisSerializer(Class<T> 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特有的SerializerFeatureParserConfig
  • 使用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 RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<Object, 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; } }

配置要点说明:

  1. 使用StringRedisSerializer处理所有key的序列化
  2. 对value使用自定义的FastJson2JsonRedisSerializer
  3. 配置ObjectMapper以支持多态类型处理
  4. 确保所有属性都正确初始化后调用afterPropertiesSet()

5. 测试与验证策略

升级完成后,必须进行全面的测试验证:

5.1 单元测试示例

@SpringBootTest public class RedisSerializerTest { @Autowired private RedisTemplate<String, 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() { Map<String, 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); Map<String, Object> cached = (Map<String, Object>) redisTemplate.opsForValue().get("complex:1"); assertNotNull(cached.get("list")); assertEquals(3, ((List)cached.get("list")).size()); } }

5.2 兼容性测试要点

  1. 基本数据类型:String、Integer、Long等
  2. 集合类型:List、Set、Map
  3. 自定义对象:包含嵌套结构的POJO
  4. 特殊字符:包含UTF-8特殊字符的字符串
  5. 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 FastJson2JsonRedisSerializer<User> 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 FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> { // ... 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 RedisSerializer<Object> { private final RedisSerializer<String> stringSerializer = new StringRedisSerializer(); private final FastJson2JsonRedisSerializer<Object> 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 性能调优建议

  1. 重用序列化器实例:避免频繁创建序列化器
  2. 合理配置缓冲区大小:对于大对象,预设合适的缓冲区
  3. 选择合适的特性:只启用必要的序列化特性
  4. 监控序列化性能:记录关键操作的耗时
// 性能优化的序列化器配置 public class OptimizedFastJson2JsonRedisSerializer<T> implements RedisSerializer<T> { private static final int INITIAL_BUFFER_SIZE = 1024; // 1KB初始缓冲区 private final Class<T> clazz; public OptimizedFastJson2JsonRedisSerializer(Class<T> 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); } } // ... 其他方法保持不变 }
http://www.jsqmd.com/news/1014496/

相关文章:

  • 从内存困境到流畅体验:PCL2启动器的智能资源管理革命
  • 电脑新手必备:从装机到日常维护的实用指南
  • Java 8老系统SQL Agent实战:AI生成候选SQL,安全引擎拦截后再执行
  • 如何让2008年以后的旧款Mac安装最新macOS?OCLP-Mod终极指南
  • 【AI Daily】AI日报 2026-06-14
  • 惊了!原来论文可以这样省时间?2026降AIGC网站推荐合集
  • 心电图特征点检测系统Matlab程序含GUI2(设计源文件+万字报告+讲解)(支持资料、图片参考_降重降ai)
  • 3分钟搞定洛雪音乐播放问题:六音音源优化版终极解决方案
  • 如何用5分钟将你的英雄联盟游戏效率提升300%:League Akari完全指南
  • 086、Claude Code 无头模式:在 CI/CD 流水线中的 headless 使用与参数配置
  • 2026年成都小吃车定制服务商TOP5盘点 - 互联网科技品牌测评
  • 牛客网Java面试题汇总(2026秋招最新版,附答案,持续更新)
  • 2026免费音频转AIFF在线保姆级教程!无限制工具手把手教学,苹果专业音频工作站专用 - 时时资讯
  • 终极AI换脸指南:3步实现专业级深度伪造,无需训练!
  • 如何永久免费使用IDM下载加速器:开源激活脚本完全指南
  • 2026这6款宝藏降AI率网站全网首测,一键让AIGC率断崖式下跌!
  • 照着用就行:一键生成论文工具2026最新测评与推荐
  • 双麦克风降噪仿真matlab程序2(设计源文件+万字报告+讲解)(支持资料、图片参考_降重降ai)
  • 影刀RPA新手教程_多账号Cookie池调度高并发采集的账号资源管理
  • [Word] 只关闭Microsoft Word动画,不关闭Windows动画的方法
  • 2026年烟台地暖服务商推荐榜:芝罘莱山新房/老房地暖,暖气地暖暖通公司专业实力与口碑深度测评 - 品牌发掘
  • 内容运营团队用智能内容分发工具_CSDN_AI数字营销适合哪类场景
  • 构建Python微服务架构:轻松应对高并发场景
  • 2026免费音频转CAF在线保姆级教程!无限制工具手把手教学,iOS系统原生核心音频格式 - 时时资讯
  • 查询每门课程最好的前两名的SQL实现
  • 7 硬件工程师笔面试高频考点真题解析——IGBT
  • 实战构建抖音批量下载器:5步掌握无水印内容自动化采集
  • 设计系统中的主题切换:从 CSS 变量到运行时主题引擎的架构实践
  • OpenCore Legacy Patcher解决方案:为老款Mac注入新生命,体验最新macOS系统
  • 2026年 东莞UV打印/亚克力UV打印/UV打印加工/UV彩白彩玻璃贴最新推荐榜单:高精度工艺与创意透光的品质之选 - 品牌发掘