package com.ruoyi.common.redis.aspectj;
|
|
import com.ruoyi.common.redis.annotation.DistributedLock;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.commons.lang3.StringUtils;
|
import org.aspectj.lang.ProceedingJoinPoint;
|
import org.aspectj.lang.annotation.Around;
|
import org.aspectj.lang.annotation.Aspect;
|
import org.aspectj.lang.annotation.Pointcut;
|
import org.aspectj.lang.reflect.MethodSignature;
|
import org.redisson.api.RLock;
|
import org.redisson.api.RedissonClient;
|
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
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 org.springframework.web.bind.annotation.GetMapping;
|
|
import javax.annotation.Resource;
|
import java.lang.reflect.Method;
|
import java.util.Objects;
|
|
/**
|
* @Description 分布式锁切面
|
* 注意!!!分布式锁不能加在事务方法当中:因为当锁释放,事务还没有提交
|
* @Author luofl
|
* @Date 2024/11/26 16:42
|
*/
|
@Aspect
|
@Component
|
@Slf4j
|
public class DistributedLockAspect {
|
@Resource
|
private RedissonClient redissonClient;
|
|
/**
|
* @Descreption: 定义切面:以注解为切面
|
* @Author: luofl
|
* @Date: 2024/11/26 16:44
|
*/
|
@Pointcut("@annotation(com.ruoyi.common.redis.annotation.DistributedLock)")
|
public void distributedLockAspect() {
|
}
|
|
@Around(value = "distributedLockAspect()")
|
public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
|
|
//切点所在的类
|
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
|
Method method = methodSignature.getMethod();
|
//
|
DistributedLock annotation = method.getAnnotation(DistributedLock.class);
|
String lockName = getLockName(annotation, pjp.getArgs(), method);
|
log.info("lockName:"+lockName);
|
RLock lock = redissonClient.getLock(lockName);
|
lock.lock();
|
try {
|
return pjp.proceed();
|
} finally {
|
if (lock.isLocked() && lock.isHeldByCurrentThread()) {
|
//释放锁
|
lock.unlock();
|
}
|
}
|
}
|
|
/**
|
* @Descreption: 获取锁名字,优先获取注解中锁名
|
* @Author: luofl
|
* @Date: 2024/11/26 16:45
|
*/
|
private String getLockName(DistributedLock distributedLock, Object[] args, Method method) {
|
//优先获取注解名称
|
if (StringUtils.isNotBlank(distributedLock.lockName())) {
|
return distributedLock.lockName();
|
}
|
//根据参数匹配有参数就使用动态参数,没有就使用定义参数
|
String lockNamePre = distributedLock.lockNamePre();
|
String lockNamePost = distributedLock.lockNamePost();
|
String separator = distributedLock.separator();
|
|
String preExpression = parseExpression(lockNamePre, method, args);
|
String postExpression = parseExpression(lockNamePost, method, args);
|
StringBuilder sb = new StringBuilder();
|
if (StringUtils.isNotBlank(preExpression)) {
|
sb.append(preExpression);
|
} else {
|
sb.append(lockNamePre);
|
}
|
sb.append(separator);
|
if (StringUtils.isNotBlank(postExpression)) {
|
sb.append(postExpression);
|
} else {
|
sb.append(lockNamePost);
|
}
|
return sb.toString();
|
}
|
|
/**
|
* el表达式解析
|
*
|
* @param expressionString 解析值
|
* @param method 方法
|
* @param args 参数
|
*/
|
private String parseExpression(String expressionString, Method method, Object[] args) {
|
//获取被拦截方法参数名列表
|
LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
|
String[] paramNameArr = discoverer.getParameterNames(method);
|
//SPEL解析
|
ExpressionParser parser = new SpelExpressionParser();
|
StandardEvaluationContext context = new StandardEvaluationContext();
|
for (int i = 0; i < Objects.requireNonNull(paramNameArr).length; i++) {
|
context.setVariable(paramNameArr[i], args[i]);
|
}
|
return parser.parseExpression(expressionString).getValue(context, String.class);
|
}
|
|
// ==========================示例=============================
|
|
//固定静态参数锁:product_lock
|
@DistributedLock(lockName = "product_lock")
|
@GetMapping(value = "/test1")
|
public void test1() {
|
System.out.println("执行事务");
|
}
|
|
//未匹配到参数,因此仍然是静态参数锁:#param1_#param2
|
@DistributedLock(lockNamePre = "#param1", lockNamePost = "#param2")
|
@GetMapping(value = "/test2")
|
public void test2() {
|
System.out.println("执行事务");
|
}
|
|
//匹配到参数,动态参数锁:hello_world
|
@DistributedLock(lockNamePre = "#order", lockNamePost = "#param2")
|
@GetMapping(value = "/test3")
|
public void test3(String param1, String param2) {
|
System.out.println("执行事务");
|
}
|
}
|