当前位置: 首页 > 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.gsyq.cn/news/1526314.html

相关文章:

  • 如何让2008年以后的旧款Mac安装最新macOS?OCLP-Mod终极指南
  • 惊了!原来论文可以这样省时间?2026降AIGC网站推荐合集
  • 2026免费音频转AIFF在线保姆级教程!无限制工具手把手教学,苹果专业音频工作站专用 - 时时资讯
  • 2026年成都小吃车定制服务商TOP5盘点 - 互联网科技品牌测评
  • 双麦克风降噪仿真matlab程序2(设计源文件+万字报告+讲解)(支持资料、图片参考_降重降ai)
  • [Word] 只关闭Microsoft Word动画,不关闭Windows动画的方法
  • 2026年烟台地暖服务商推荐榜:芝罘莱山新房/老房地暖,暖气地暖暖通公司专业实力与口碑深度测评 - 品牌发掘
  • 7 硬件工程师笔面试高频考点真题解析——IGBT
  • 实战构建抖音批量下载器:5步掌握无水印内容自动化采集
  • 激活函数实战指南:从梯度消失到硬件部署的全链路决策
  • 硬盘空间神秘消失?dupeGuru帮你揪出重复文件元凶
  • 技术方案:解决网盘直链下载的跨平台集成挑战
  • 下一代金融数据处理系统:实时订单簿重建技术深度解析
  • 深入解析dex2jar:从Dalvik字节码到Java字节码的专业转换引擎
  • 2026免费音频转AC3在线保姆级教程!无限制工具手把手教学,杜比数字环绕声制作必备 - 时时资讯
  • Java毕设选题推荐:基于 B/S 架构的调查问卷管理系统的设计与实现 基于 Spring Boot 的轻量化问卷采集系统的设计与实现【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 2026免费视频转MOV在线保姆级教程!无限制工具手把手教学,苹果Final Cut Pro直接导入 - 时时资讯
  • MPC7450处理器信号接口深度解析:L3缓存、中断与时钟配置实战
  • 后ChatGPT时代的杀手级应用:会使用工具的AI Agent产品预测
  • 如何构建互动桌面宠物:打造响应式Live2D动画系统
  • 5分钟快速上手ChatWiki:开源知识库系统的完整使用指南
  • MPC8540以太网控制器:地址识别、哈希过滤与缓冲区描述符详解
  • 云计算学习中心第四次作业
  • AXOrderBook:如何用Python+FPGA重建A股千档订单簿实现高频交易优势
  • 自动化提示词优化算法在 Harness 中的集成
  • Java毕设选题推荐:基于 SpringBoot 技术栈的健康档案管理系统的设计与实现 轻量化个人健康数据管理平台【附源码、mysql、文档、调试+代码讲解+全bao等】
  • 2026免费视频转FLV在线保姆级教程!无限制工具手把手教学,网页流媒体极速加载 - 时时资讯
  • SQL Server Always On实战:从数据库备份还原到AG配置完成的完整数据同步流水线
  • 100公斤寄德邦还是安能更便宜?100公斤大件寄德邦还是安能?比价省一半 - 快递物流资讯
  • Path of Building PoE2:5分钟掌握流放之路2终极角色构建方案