news 2026/6/14 22:17:57

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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); } } // ... 其他方法保持不变 }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/14 22:13:37

111.扩散模型训练稳定性剖析|对比GAN模式崩溃的核心优势

摘要 扩散模型是当前生成式AI领域最核心的技术之一,在图像生成、音频合成、分子设计等领域展现出超越GAN和VAE的性能。本文从数学原理出发,详细推导了扩散模型的前向加噪与反向去噪过程,并基于PyTorch实现了一个完整的DDPM(Denoising Diffusion Probabilistic Models)训练…

作者头像 李华
网站建设 2026/6/14 22:12:08

实战构建抖音批量下载器:5步掌握无水印内容自动化采集

实战构建抖音批量下载器&#xff1a;5步掌握无水印内容自动化采集 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallback suppo…

作者头像 李华
网站建设 2026/6/14 22:09:59

跳表:高效查找的链表黑科技

引言 在前面数据结构系列中&#xff0c;我们学过二分查找——在有序数组中查找一个数&#xff0c;时间复杂度 O(log n)&#xff0c;非常快。但如果数据存储在链表中呢&#xff1f;链表不支持随机访问&#xff0c;只能从头一个个找&#xff0c;查找退化到 O(n)。 有没有办法让…

作者头像 李华
网站建设 2026/6/14 22:07:05

学 ArkUI 传感器(专题二):从加速度计到指南针,玩转硬件能力

&#x1f4e1; 零基础学 ArkUI 传感器&#xff08;专题二&#xff09;&#xff1a;从加速度计到指南针&#xff0c;玩转硬件能力 博主说&#xff1a; 你的手机里藏着十几个传感器——加速度计、陀螺仪、地磁、光线、距离、气压……在 ArkUI 中调用它们只需要几行代码&#xff0…

作者头像 李华
网站建设 2026/6/14 22:02:00

MPC7450缓存架构与MPX总线设计:从原理到工程实践

1. 项目概述&#xff1a;从缓存原理到MPC7450的工程实践在处理器设计的漫长演进史中&#xff0c;缓存&#xff08;Cache&#xff09;技术始终是平衡性能与成本的核心杠杆。简单来说&#xff0c;它的工作原理就像是你书桌上一个专门放常用书籍和资料的小书架。当你需要查阅某个资…

作者头像 李华
网站建设 2026/6/14 21:52:13

【鸿蒙原生应用开发实战】第二篇:数据模型与状态管理 — 彻底搞懂 ArkTS 的数据驱动机制

【鸿蒙原生应用开发实战】第二篇&#xff1a;数据模型与状态管理 — 彻底搞懂 ArkTS 的数据驱动机制 前言 在上一篇中我们搭建了项目架构并完成了首页开发。这一篇我们将深入 ArkTS 的核心 — 数据模型定义 与 状态管理机制。这是整个应用的"发动机"&#xff1a;数据…

作者头像 李华