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