先上效果,rediskey 为第一个从参数的id 最大等待时间为5000毫秒,占用锁之后的自动释放时间为30秒,如果30秒内方法体执行完成,AOP代码会手动释放锁,但是对于写业务的人来说是透明的。
一丶先定义一个注解
package com.util.lock;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
* @description: redis分布式锁
* @createTime: 2022-06-08 09:43
**/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DistributedLock {
/**
* 锁的名称: 支持spel表达式 eg:#tenantDo.id
* 默认为:类全限定名+'.'+方法名+':' + lockKey eg: com.lzkj.up.service.impl.UserService.getUser:10
*/
String lockKey();
/**
* lockKeyPrefix为空时,lockKey使用默认前缀:类全限定名+'.'+方法名+':'
* lockKeyPrefix不为空时,使用lockKeyPrefix作为前缀
* @return
*/
String lockKeyPrefix() default "";
/**
* 最长等待时间。默认3秒
* 该字段只有当tryLock()返回true才有效。
*/
long waitTime() default 3L;
/**
* 锁超时时间。默认5秒
* 如果tryLock为false,且leaseTime设置为0及以下,会变成lock()
*/
long leaseTime() default 30L;
/**
* 时间单位。默认为秒。
*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
}
二丶实现aop切面逻辑,使用redission框架
package com.lock;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* @description: redis分布式锁 aop实现
* @createTime: 2022-06-08 09:54
**/
@Aspect
@Component
@Slf4j
public class DistributedLockAspect {
@Autowired
private Redisson redissonClient;
@Around("@annotation(com.lzkj.up.util.lock.DistributedLock)")
public Object doLock(ProceedingJoinPoint joinPoint) throws Throwable {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method method = methodSignature.getMethod();
DistributedLock distributedLock = method.getAnnotation(DistributedLock.class);
String lockKey = distributedLock.lockKey();
//获取方法的形参名数组,spring常用,rpc时不需要,反射也不需要。表达式中写形参名
LocalVariableTableParameterNameDiscoverer nameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
String[] parameterNames = nameDiscoverer.getParameterNames(method);
String finalKey = "";
String className = joinPoint.getSignature().getDeclaringTypeName();
Map<String, Object> hashMap = new HashMap<>();
if (parameterNames != null && parameterNames.length > 0) {
//spel表达式解析
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = new StandardEvaluationContext();
Expression expression = parser.parseExpression(lockKey);
//存储形参和传值的映射
//获取方法传入的参数值
Object[] args = joinPoint.getArgs();
for (int i = 0; i < parameterNames.length; i++) {
//建立形参名和值的映射关系,存入上下文当中,后面取出表达式带有的形参
context.setVariable(parameterNames[i], args[i]);
hashMap.put(parameterNames[i], args[i]);
}
//这里取字符串类型作为key
String newKey = expression.getValue(context, String.class);
finalKey = className + "." + method.getName() + ":" + newKey;
} else {
finalKey = className + "." + method.getName() + ":" + lockKey;
}
log.info("DistributedLock finalKey=[{}]开始上锁,参数为:{}", finalKey, JSONObject.toJSONString(hashMap));
RLock lock = redissonClient.getLock(finalKey);
boolean res = lock(lock, distributedLock);
long currentThreadId = Thread.currentThread().getId();
if (res) {
log.info("获取锁成功:{}",currentThreadId);
try {
log.info("DistributedLock finalKey=[{}],方法运行结束", finalKey);
return joinPoint.proceed();
} catch (Throwable e) {
log.error("DistributedLock: finalKey: {} 业务异常", finalKey);
throw new BusinessException("500",e.getMessage());
} finally {
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
} else {
log.warn("DistributedLock: finalKey: {} 获取锁失败:{}",finalKey,currentThreadId);
throw new BusinessException("400","获取锁失败,请稍后重试");
}
}
private boolean lock(RLock lock, DistributedLock distributedLock) {
boolean result;
// 上锁
try {
result = lock.tryLock(distributedLock.waitTime(), distributedLock.leaseTime(), distributedLock.timeUnit());
} catch (InterruptedException e) {
log.error("DistributedLock InterruptedException",e);
result = false;
}
return result;
}
}

博客介绍了如何使用 Redisson 框架实现一个基于注解的分布式锁,包括定义自定义注解 `DistributedLock`,设置锁的名称、等待时间和超时时间等参数,并在 AOP 切面中解析注解内容,进行加锁、解锁操作,确保并发控制的安全性。
681

被折叠的 条评论
为什么被折叠?



