package com.panzhihua.sangeshenbian.aspectj; import com.panzhihua.sangeshenbian.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 javax.annotation.Resource; import java.lang.reflect.Method; import java.util.Objects; /** * @Desecription: 分布式锁切面 * 注意!!!分布式锁不能加在事务方法当中:因为当锁释放,事务还没有提交 */ @Aspect @Component @Slf4j public class DistributedLockAspect { @Resource private RedissonClient redissonClient; /** * @Descreption: 定义切面:以注解为切面 */ @Pointcut("@annotation(com.panzhihua.sangeshenbian.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: 获取锁名字,优先获取注解中锁名 */ 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 参数 * @return */ 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("执行事务"); } }