1+ package me .chanjar .weixin .common .util .locks ;
2+
3+ import org .springframework .data .redis .connection .jedis .JedisConnectionFactory ;
4+ import org .springframework .data .redis .core .StringRedisTemplate ;
5+ import org .springframework .data .redis .serializer .StringRedisSerializer ;
6+ import org .testng .annotations .BeforeTest ;
7+ import org .testng .annotations .Test ;
8+
9+ import static org .testng .Assert .*;
10+
11+ /**
12+ * 测试 RedisTemplateSimpleDistributedLock 在自定义 Key 序列化时的兼容性
13+ *
14+ * 这个测试验证修复后的实现确保 tryLock 和 unlock 使用一致的键序列化方式
15+ */
16+ @ Test (enabled = false ) // 默认禁用,需要Redis实例才能运行
17+ public class RedisTemplateSimpleDistributedLockSerializationTest {
18+
19+ private RedisTemplateSimpleDistributedLock redisLock ;
20+ private StringRedisTemplate redisTemplate ;
21+
22+ @ BeforeTest
23+ public void init () {
24+ JedisConnectionFactory connectionFactory = new JedisConnectionFactory ();
25+ connectionFactory .setHostName ("127.0.0.1" );
26+ connectionFactory .setPort (6379 );
27+ connectionFactory .afterPropertiesSet ();
28+
29+ // 创建一个带自定义键序列化的 StringRedisTemplate
30+ StringRedisTemplate redisTemplate = new StringRedisTemplate (connectionFactory );
31+
32+ // 使用自定义键序列化器,模拟在键前面添加前缀的场景
33+ redisTemplate .setKeySerializer (new StringRedisSerializer () {
34+ @ Override
35+ public byte [] serialize (String string ) {
36+ if (string == null ) return null ;
37+ // 添加 "System:" 前缀,模拟用户自定义的键序列化
38+ return super .serialize ("System:" + string );
39+ }
40+
41+ @ Override
42+ public String deserialize (byte [] bytes ) {
43+ if (bytes == null ) return null ;
44+ String result = super .deserialize (bytes );
45+ // 移除前缀进行反序列化
46+ return result != null && result .startsWith ("System:" ) ? result .substring (7 ) : result ;
47+ }
48+ });
49+
50+ this .redisTemplate = redisTemplate ;
51+ this .redisLock = new RedisTemplateSimpleDistributedLock (redisTemplate , "test_lock_key" , 60000 );
52+ }
53+
54+ @ Test (description = "测试自定义键序列化器下的锁操作一致性" )
55+ public void testLockConsistencyWithCustomKeySerializer () {
56+ // 1. 获取锁应该成功
57+ assertTrue (redisLock .tryLock (), "第一次获取锁应该成功" );
58+ assertNotNull (redisLock .getLockSecretValue (), "锁值应该存在" );
59+
60+ // 2. 验证键已正确存储(通过 redisTemplate 直接查询)
61+ String actualValue = redisTemplate .opsForValue ().get ("test_lock_key" );
62+ assertEquals (actualValue , redisLock .getLockSecretValue (), "通过 redisTemplate 查询的值应该与锁值相同" );
63+
64+ // 3. 再次尝试获取同一把锁应该成功(可重入)
65+ assertTrue (redisLock .tryLock (), "可重入锁应该再次获取成功" );
66+
67+ // 4. 释放锁应该成功
68+ redisLock .unlock ();
69+ assertNull (redisLock .getLockSecretValue (), "释放锁后锁值应该为空" );
70+
71+ // 5. 验证键已被删除
72+ actualValue = redisTemplate .opsForValue ().get ("test_lock_key" );
73+ assertNull (actualValue , "释放锁后 Redis 中的键应该被删除" );
74+
75+ // 6. 释放已释放的锁应该是安全的
76+ redisLock .unlock (); // 不应该抛出异常
77+ }
78+
79+ @ Test (description = "测试不同线程使用相同键的锁排他性" )
80+ public void testLockExclusivityWithCustomKeySerializer () throws InterruptedException {
81+ // 第一个锁实例获取锁
82+ assertTrue (redisLock .tryLock (), "第一个锁实例应该成功获取锁" );
83+
84+ // 创建第二个锁实例使用相同的键
85+ RedisTemplateSimpleDistributedLock anotherLock = new RedisTemplateSimpleDistributedLock (
86+ redisTemplate , "test_lock_key" , 60000 );
87+
88+ // 第二个锁实例不应该能获取锁
89+ assertFalse (anotherLock .tryLock (), "第二个锁实例不应该能获取已被占用的锁" );
90+
91+ // 释放第一个锁
92+ redisLock .unlock ();
93+
94+ // 现在第二个锁实例应该能获取锁
95+ assertTrue (anotherLock .tryLock (), "第一个锁释放后,第二个锁实例应该能获取锁" );
96+
97+ // 清理
98+ anotherLock .unlock ();
99+ }
100+ }
0 commit comments