package com.data.aspect;
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.reflect.MethodSignature;
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 javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.UUID;
@Slf4j
@Component
@Aspect
public class DistributedRedisLockAspect {
private static final ThreadLocal<String> LOCK_VALUE = ThreadLocal.withInitial(() -> UUID.randomUUID().toString());
private static final String FORMAT = "%s:%s";
@Resource
Lock lock;
/**
* SpEL 解析器
*/
private ExpressionParser parser = new SpelExpressionParser();
/**
* 用于获取参数名
*/
private LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
@Around("@annotation(com.data.DistributedRedisLock)")
public Object around(ProceedingJoinPoint point) throws Throwable {
Object result;
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
Object[] args = point.getArgs();
DistributedRedisLock redisLockAnnotation = method.getAnnotation(DistributedRedisLock.class);
// key 前缀
String preKey = redisLockAnnotation.preKey();
String key = redisLockAnnotation.key();
long timeout = redisLockAnnotation.timeout();
// 生成缓存 Key
String cacheKey = this.generatorKey(preKey, key, method, args);
// 生成当前锁的唯一标识
String value = LOCK_VALUE.get();
lock.lock(cacheKey, value, timeout);
try {
result = point.proceed();
} finally {
// 只有 key value 完全匹配才能解锁
lock.unlock(cacheKey, value);
}
return result;
}
private String generatorKey(String pre, String key, Method method, Object[] args) {
//获取方法参数名
String[] params = discoverer.getParameterNames(method);
//将参数纳入Spring管理
EvaluationContext context = new StandardEvaluationContext();
for (int len = 0; len < params.length; len++) {
context.setVariable(params[len], args[len]);
}
Expression expression = parser.parseExpression(key);
String spelValue = expression.getValue(context, String.class);
return String.format(FORMAT, pre, spelValue);
}
}