前言
抢红包功能实现主要包括发红包、抢红包、记红包和拆红包四块。
教程来源:BV13R4y1v7sP
配置说明
JDK21
相关代码
Pom.xml
| |
RedisConfig.java
| |
RedisKeyConstants.java
| |
RedPackageController.java
| |
ApiResponse.java
| |
RedPackageServiceImpl.java
| |
RedPackageService.java
| |
DemoApplication.java
| |
application.yaml
| |
测试
访问http://localhost:8080/red/send?totalMoney=100&redpackageNumber=5,输出结果如下所示:
| |
访问http://localhost:8080/red/rob?redpackageKey=1abe520c30dc4ec0b4153e3708c01fd3&userId=2,输出结果如下所示:
| |
注意:这里的ID要和上面的一致
附录——深入了解 RedisTemplate 的 leftPushAll 与序列化问题
在使用 Spring Data Redis 时,RedisTemplate 提供了丰富的操作方法,其中 leftPushAll(左侧批量推送)是一个常用的列表操作方法。本文围绕两个常见疑问展开:① leftPushAll 能否传入 Object...、Collection、以及为什么有时需要将 ArrayList 转换为数组;② RedisTemplate 的序列化问题以及潜在的隐性 Bug。
- 使用
leftPushAll时,理论上可以用两种形式:- 变参形式:
leftPushAll(key, value1, value2, ...) - 集合形式:
leftPushAll(key, collection)
- 变参形式:
- 如果遇到将
ArrayList直接传入无法生效的情况,可以尝试将集合转换为数组再调用,作为一种兼容性手段,但并非必要的做法,优先确保版本与重载匹配正确。 - 序列化是影响 Redis 数据可用性的关键因素。统一、明确的序列化策略能避免大多数问题。建议使用
String键 + JSON(或自定义序列化器)的组合,并确保在写入和读取时使用同样的序列化配置。
1) RedisTemplate 中 leftPushAll 的参数形式
支持的参数形式
leftPushAll(K key, V... values):可以传一组可变参数,等价于将多个值逐个推入到左边。leftPushAll(K key, Collection<V> values):也可以传入一个Collection,将集合中的元素逐个推入到左边。
常见的困惑点
- 有些开发者在实践中遇到将
ArrayList<V>直接作为参数传入时失败的问题,或者感觉“不工作”。其实问题通常出在 Java 方法重载分辨的类型匹配、或者序列化导致的写入失败,而不一定是 API 自身对Collection的支持问题。 - 关键点在于:确保传入的类型与 RedisTemplate 的泛型参数一致,并且底层的序列化策略能够正确序列化这些对象。
- 有些开发者在实践中遇到将
为什么有时需要把
ArrayList转换为数组V[]?- 某些版本的
RedisTemplate的重载实现或编译期行为,可能在编译时推断出具体的V... values的数组形式,从而更直接地匹配到leftPushAll(K, V...)。如果传入的是Collection<V>,理论上也应工作,但在具体实现或版本差异上,可能出现边缘情况导致调用不走你期望的重载。 - 将
ArrayList<V>手动转换为V[](例如values.toArray((V[]) new Object[0]))可以确保调用到你明确的变参重载,从而规避某些版本的坑。但这并非必要的普遍做法,更多的是一个“保险性”的兼容手段。
- 某些版本的
实操建议
优先使用你熟悉的形式:
redisTemplate.opsForList().leftPushAll(key, valuesArray),其中valuesArray为V[]。- 或者
redisTemplate.opsForList().leftPushAll(key, collection),直接传Collection<V>。
如果遇到某个版本出现“无法调用正确的重载”或运行时异常,尝试将 Collection 转换为数组再调用一次:
1List<V> list = new ArrayList<>();// filling list ...V[] arr = (V[]) list.toArray(new Object[0]);redisTemplate.opsForList().leftPushAll(key, arr);但请注意:直接将
List<V>转换为V[]时,需要确保类型安全,避免ClassCastException。在 Java 泛型擦除的场景下,通常使用适配代码或者通过具体类型实例化一个正确类型的数组。
2) RedisTemplate 的序列化问题
为什么序列化很关键?
- RedisTemplate 在往 Redis 写入数据前,会把 Java 对象序列化成字节数组。读取时再把字节数组反序列化成 Java 对象。序列化策略直接决定了你能否正确存取、检索和比较数据。
- 常见序列化策略有:JDK 序列化、JSON(如
Jackson2JsonRedisSerializer)、String 序列化、FastJSON 等。
常见的问题点
- 序列化不一致:写入时使用一种序列化方式,读取时使用另一种,可能导致反序列化失败或数据不可读。
- 非对称的对象结构:自定义对象若没有正确的序列化实现(如缺少无参构造、缺失
Serializable等),可能在反序列化时出错。 - 字符串化 vs 对象化:把对象错误地序列化成字符串,取回时需要再次解析,容易出错。
- 存入原始字节但读取时却以对象方式处理,或对同一 Key 使用了不同的 Template(不同的序列化策略)导致数据不可用。
如何正确配置序列化?
- 统一的序列化策略是关键。推荐做法:
- 对键使用
StringRedisSerializer,对值使用Jackson2JsonRedisSerializer(或自定义的 JSON 序列化器),对哈希键/值也保持一致。 - 为自定义对象定义明确的序列化/反序列化配置,确保对象具备可序列化的结构。
- 对键使用
- 示例配置(简化版,基于 Spring Boot):
| |
注意:如果你明确知道存入的是某一类具体对象,可以将 Jackson2JsonRedisSerializer 的泛型改为该对象类型,以提升反序列化的安全性和效率。