|
@@ -0,0 +1,114 @@
|
|
|
+package cn.ezhizao.framework.aspectj.lang;
|
|
|
+
|
|
|
+import cn.ezhizao.common.utils.ELUtils;
|
|
|
+import cn.ezhizao.common.utils.SecurityUtils;
|
|
|
+import cn.ezhizao.framework.aspectj.lang.annotation.distributedLock;
|
|
|
+import lombok.extern.log4j.Log4j2;
|
|
|
+import org.aspectj.lang.ProceedingJoinPoint;
|
|
|
+import org.aspectj.lang.annotation.Around;
|
|
|
+import org.aspectj.lang.annotation.Aspect;
|
|
|
+import org.redisson.api.RLock;
|
|
|
+import org.redisson.api.RedissonClient;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|
|
+import org.springframework.context.ApplicationContext;
|
|
|
+import org.springframework.expression.ExpressionParser;
|
|
|
+import org.springframework.expression.spel.standard.SpelExpressionParser;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import java.util.concurrent.ConcurrentHashMap;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+import java.util.concurrent.locks.ReentrantLock;
|
|
|
+
|
|
|
+@Component
|
|
|
+@Aspect
|
|
|
+@Log4j2
|
|
|
+@ConditionalOnProperty(name = "cache.type", havingValue = "redis", matchIfMissing = true)
|
|
|
+public class redisLockAspect {
|
|
|
+ @Autowired
|
|
|
+ private RedissonClient redissonClient;
|
|
|
+ private final ExpressionParser parser = new SpelExpressionParser();
|
|
|
+ private final ConcurrentHashMap<String, ReentrantLock> lockMap = new ConcurrentHashMap<>();
|
|
|
+ @Autowired
|
|
|
+ private ApplicationContext context;
|
|
|
+ @Around("@annotation(distributedLock)")
|
|
|
+ public Object around(ProceedingJoinPoint joinPoint, distributedLock distributedLock) throws Throwable {
|
|
|
+ return distributed(joinPoint, distributedLock);
|
|
|
+ }
|
|
|
+ private Object distributed(ProceedingJoinPoint joinPoint, distributedLock distributedLock) throws Throwable {
|
|
|
+ // 获取 SpEL 表达式的 key 值
|
|
|
+ // 解析 prefix 和 key
|
|
|
+ String prefix = ELUtils.parseExpression(distributedLock.prefix(), joinPoint);
|
|
|
+ String key = distributedLock.key();
|
|
|
+ if(distributedLock.key().startsWith("@")){
|
|
|
+// Object bean = context.getBean(key.substring(1,key.indexOf(".")));
|
|
|
+// Object o = SpringUtils.getBean(key.substring(1,key.indexOf(".")));
|
|
|
+// Method method = bean.getClass().getMethod(key.substring(key.indexOf(".")+1,key.indexOf("(")));需要参数列表放弃
|
|
|
+// method.invoke(MethodSignatureMatcher.splitParameters(key));
|
|
|
+ if(key.contains("tenantId")){
|
|
|
+ key = String.valueOf(SecurityUtils.getTenantId());
|
|
|
+ }
|
|
|
+ }else if(distributedLock.key().startsWith("#")){
|
|
|
+ key = ELUtils.parseExpression(distributedLock.key(), joinPoint);
|
|
|
+ }
|
|
|
+
|
|
|
+ String lockKey = prefix + key; // 组合成完整的锁 key
|
|
|
+ long leaseTime = distributedLock.leaseTime();
|
|
|
+ TimeUnit timeUnit = distributedLock.leaseTimeUnit();
|
|
|
+ RLock lock = redissonClient.getLock(lockKey);
|
|
|
+
|
|
|
+ log.info("start lock {}", lockKey);
|
|
|
+ try {
|
|
|
+ // 尝试获取锁
|
|
|
+ if(lock.isLocked()){
|
|
|
+ throw new RuntimeException(distributedLock.errorMsg());
|
|
|
+ }else{
|
|
|
+ if(lock.tryLock(distributedLock.waitTime(), leaseTime, timeUnit)){
|
|
|
+ return joinPoint.proceed();
|
|
|
+ }else{
|
|
|
+ throw new RuntimeException("加锁失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ if(lock.isHeldByCurrentThread()){
|
|
|
+ log.info("end lock {}", lockKey);
|
|
|
+ // 只有是当前线程才去释放锁
|
|
|
+ lock.unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ private Object alone(ProceedingJoinPoint joinPoint, distributedLock distributedLock) throws Throwable {
|
|
|
+ // 解析 prefix 和 key
|
|
|
+ String prefix = ELUtils.parseExpression(distributedLock.prefix(), joinPoint);
|
|
|
+ String key = ELUtils.parseExpression(distributedLock.key(), joinPoint);
|
|
|
+ String lockKey = prefix + key; // 组合成完整的锁 key
|
|
|
+ // 获取 leaseTime
|
|
|
+ long leaseTime = distributedLock.leaseTime();
|
|
|
+ TimeUnit timeUnit = distributedLock.leaseTimeUnit();
|
|
|
+
|
|
|
+ // 从 map 中获取锁
|
|
|
+ ReentrantLock lock = lockMap.computeIfAbsent(lockKey, k -> new ReentrantLock());
|
|
|
+ boolean acquired = false;
|
|
|
+
|
|
|
+ log.info("Attempting to lock {}", lockKey);
|
|
|
+ try {
|
|
|
+ // 尝试获取锁,设置等待时间
|
|
|
+ acquired = lock.tryLock(leaseTime, timeUnit);
|
|
|
+ if (acquired) {
|
|
|
+ log.info("Lock acquired: {}", lockKey);
|
|
|
+ // 获取到锁,执行方法
|
|
|
+ return joinPoint.proceed();
|
|
|
+ } else {
|
|
|
+ log.warn("Unable to acquire lock for key: {}", lockKey);
|
|
|
+ throw new RuntimeException("Unable to acquire lock for key: " + lockKey);
|
|
|
+ }
|
|
|
+ } finally {
|
|
|
+ if (acquired) {
|
|
|
+ lock.unlock();
|
|
|
+ log.info("Lock released: {}", lockKey);
|
|
|
+ // 锁释放后,移除锁对象,避免内存泄漏
|
|
|
+ lockMap.remove(lockKey);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|