package com.izouma.nineth.aspect; import com.izouma.nineth.annotations.RedisLock; import com.izouma.nineth.exception.BusinessException; import lombok.extern.slf4j.Slf4j; 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.beans.factory.annotation.Autowired; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.data.redis.core.BoundValueOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.expression.EvaluationContext; 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.Optional; @Aspect @Component @Slf4j public class RedisLockAspect { private final DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer(); private final RedissonClient redissonClient; public RedisLockAspect(RedissonClient redissonClient) { this.redissonClient = redissonClient; } @Pointcut("@annotation(com.izouma.nineth.annotations.RedisLock)") public void redisLockPointCut() { } @Around(value = "redisLockPointCut() && @annotation(redisLock)") public Object redisLock(ProceedingJoinPoint joinPoint, RedisLock redisLock) { ExpressionParser parser = new SpelExpressionParser(); EvaluationContext context = new StandardEvaluationContext(joinPoint.getSignature()); MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Method method = methodSignature.getMethod(); String[] paramNames = nameDiscoverer.getParameterNames(method); Object[] args = joinPoint.getArgs(); for (int i = 0; i < args.length; i++) { context.setVariable(paramNames[i], args[i]); } String key = redisLock.value(); try { key = Optional.ofNullable(parser.parseExpression(redisLock.value()).getValue(context)) .map(Object::toString) .orElse("default"); } catch (Exception ignored) { } log.info("enter redisLock aspect key: {}", key); RLock lock = redissonClient.getLock(key); if (redisLock.behavior() == RedisLock.Behavior.WAIT) { lock.lock(redisLock.expire(), redisLock.unit()); log.info("get redisLock success"); } else { if (!lock.tryLock()) { log.info("get redisLock fail"); throw new BusinessException(redisLock.message()); } log.info("get redisLock success"); } Object res = null; try { res = joinPoint.proceed(); try { lock.unlock(); } catch (Exception ignored) { } } catch (Throwable e) { try { lock.unlock(); } catch (Exception ignored) { } if (e instanceof BusinessException) { throw (BusinessException) e; } throw new RuntimeException(e); } return res; } }