基於redis分散式鎖註解實現
阿新 • • 發佈:2021-08-04
-
1、編寫註解
-
1、編寫註解
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documentedpublic @interface RedisLock { String key(); // 併發鎖key String value(); // 鎖定時長 預設單位秒 long ttl() default 5; }
2、編寫切面
import java.lang.reflect.Method; import java.util.concurrent.TimeUnit; 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.springframework.beans.factory.annotation.Autowired; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.stereotype.Component; import com.yun.common.api.redis.RedisService; import com.yun.common.base.response.BusinessException; import com.yun.common.base.utils.AssertUtil; import lombok.extern.slf4j.Slf4j; @Aspect @Component @Slf4j public class RedisLockAspect { @Autowired private RedisService redisService; @Pointcut("execution(public * com.bgy.*.app.*.service..*.*(..))")public void appPointCut(){}; @Pointcut("execution(public * com.bgy.*.domain..*.*(..))") public void domainPointCut(){}; @Around("(appPointCut() && @annotation(redisLock)) || (domainPointCut() && @annotation(redisLock)))") public Object before(ProceedingJoinPoint pJoinPoint, RedisLock redisLock) throws Throwable { String key = redisLock.key(); String value = redisLock.value(); long time = redisLock.ttl(); AssertUtil.notNull(key, "獲取併發鎖key為空。", key); AssertUtil.notNull(value, "獲取併發鎖value為空。", value); value = this.getRedisKey(pJoinPoint, value); String lockKey = key.concat(value); // 1、獲取鎖 RLock lock = redisService.getRLock(lockKey); // 2、鎖定 try { AssertUtil.isTrue(lock.tryLock(time, TimeUnit.SECONDS), "獲取併發鎖[%s]失敗。", lockKey); // 3、業務邏輯 return pJoinPoint.proceed(); } catch (BusinessException e) { log.error("獲取鎖失敗。", e); throw e; }finally { // 4、釋放鎖 lock.unlock(); } } private String getRedisKey(ProceedingJoinPoint pJoinPoint, String key) { //使用SpringEL表示式解析註解上的key SpelExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression(key); //獲取方法入參 Object[] parameterValues = pJoinPoint.getArgs(); //獲取方法形參 MethodSignature signature = (MethodSignature)pJoinPoint.getSignature(); Method method = signature.getMethod(); DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer(); String[] parameterNames = nameDiscoverer.getParameterNames(method); if (parameterNames == null || parameterNames.length == 0) { //方法沒有入參,直接返回註解上的key return key; } //解析表示式 EvaluationContext evaluationContext = new StandardEvaluationContext(); // 給上下文賦值 for(int i = 0 ; i < parameterNames.length ; i++) { evaluationContext.setVariable(parameterNames[i], parameterValues[i]); } try { Object expressionValue = expression.getValue(evaluationContext); if (expressionValue != null && !"".equals(expressionValue.toString())) { //返回el解析後的key return expressionValue.toString(); }else{ //使用註解上的key return key; } } catch (Exception e) { //解析失敗,預設使用註解上的key return key; } } }