分布式锁切面(DistributeLockAspect)代码解读
- 这是一个基于Spring AOP和Redisson实现的分布式锁切面,用于在方法执行前获取分布式锁,执行后释放锁。下面我将从多个维度详细解析这段代码:
一、核心功能概述
该切面主要实现以下功能:
通过@DistributeLock注解标记需要加锁的方法
支持动态生成锁的key(使用SpEL表达式)
提供多种加锁策略(立即加锁、尝试加锁、带过期时间的锁)
确保锁的最终释放(在finally块中)
二、代码结构解析
类定义与依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16@Aspect
@Component
@Order(Integer.MIN_VALUE) // 确保最先执行
public class DistributeLockAspect {
private RedissonClient redissonClient; // Redisson客户端
// 构造函数注入
public DistributeLockAspect(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
}
@Aspect:声明这是一个切面类
@Order(Integer.MIN_VALUE):确保这个切面在调用链中最早执行
通过构造函数注入RedissonClient,用于操作分布式锁核心切面方法
1
2
3
4
5@Around("@annotation(cn.hollis.nft.turbo.lock.DistributeLock)")
public Object process(ProceedingJoinPoint pjp) throws Exception
使用@Around注解拦截所有带有@DistributeLock注解的方法
ProceedingJoinPoint参数可以获取被拦截方法的信息
三、关键实现逻辑
- 锁Key的生成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17String key = distributeLock.key();
if (DistributeLockConstant.NONE_KEY.equals(key)) {
// 使用SpEL表达式动态生成key
SpelExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression(distributeLock.keyExpression());
// 设置上下文变量
EvaluationContext context = new StandardEvaluationContext();
Object[] args = pjp.getArgs();
String[] parameterNames = discoverer.getParameterNames(method);
// 绑定参数到上下文
for (int i = 0; i < parameterNames.length; i++) {
context.setVariable(parameterNames[i], args[i]);
}
key = String.valueOf(expression.getValue(context));
}
String scene = distributeLock.scene();
String lockKey = scene + "#" + key; // 最终锁key
支持静态key和动态key两种方式
动态key使用SpEL表达式,可以从方法参数中取值
最终key格式为scene#key,便于分类管理
- 加锁逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32RLock rLock= redissonClient.getLock(lockKey);
try {
boolean lockResult = false;
if (waitTime == DistributeLockConstant.DEFAULT_WAIT_TIME) {
// 立即加锁逻辑
if (expireTime == DistributeLockConstant.DEFAULT_EXPIRE_TIME) {
rLock.lock(); // 永久锁
} else {
rLock.lock(expireTime, TimeUnit.MILLISECONDS); // 带过期时间的锁
}
lockResult = true;
} else {
// 尝试加锁逻辑
if (expireTime == DistributeLockConstant.DEFAULT_EXPIRE_TIME) {
lockResult = rLock.tryLock(waitTime, TimeUnit.MILLISECONDS);
} else {
lockResult = rLock.tryLock(waitTime, expireTime, TimeUnit.MILLISECONDS);
}
}
if (!lockResult) {
throw new DistributeLockException("acquire lock failed... key : " + lockKey);
}
// 执行目标方法
response = pjp.proceed();
} finally {
// 释放锁
if (rLock.isHeldByCurrentThread()) {
rLock.unlock();
}
}
支持四种加锁模式:
永久锁(无过期时间)
带过期时间的锁
尝试获取锁(带等待时间)
尝试获取带过期时间的锁(带等待时间和过期时间)
确保锁最终会被释放(finally块)
四、设计亮点
灵活的锁Key生成:支持静态key和SpEL动态key
多种加锁策略:满足不同业务场景需求
线程安全:确保只有持有锁的线程才能释放锁
日志完备:记录加锁、释放锁的关键信息
异常处理:加锁失败抛出特定异常
五、使用示例
1 | @Service |
六、改进建议
锁的可重入性:Redisson的RLock本身支持可重入,但可以增加重入计数日志
锁的自动续期:对于长时间任务,可以实现锁的自动续期
更细粒度的异常处理:区分业务异常和锁相关的异常
性能监控:添加锁获取时间、持有时间等监控指标
这个切面实现了一个完整且健壮的分布式锁解决方案,可以很好地应用于需要分布式锁的业务场景中。