|  | @ -0,0 +1,178 @@
 | 
	
		
			
				|  |  | package com.yihu.jw.care.aop;
 | 
	
		
			
				|  |  | 
 | 
	
		
			
				|  |  | 
 | 
	
		
			
				|  |  | import com.google.common.base.Joiner;
 | 
	
		
			
				|  |  | import com.yihu.jw.care.exception.BusinessException;
 | 
	
		
			
				|  |  | import com.yihu.jw.care.service.common.RedisService;
 | 
	
		
			
				|  |  | import com.yihu.jw.care.util.RedisConstant;
 | 
	
		
			
				|  |  | import org.apache.commons.codec.digest.DigestUtils;
 | 
	
		
			
				|  |  | 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.slf4j.Logger;
 | 
	
		
			
				|  |  | import org.slf4j.LoggerFactory;
 | 
	
		
			
				|  |  | import org.springframework.beans.factory.annotation.Autowired;
 | 
	
		
			
				|  |  | import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
 | 
	
		
			
				|  |  | import org.springframework.core.ParameterNameDiscoverer;
 | 
	
		
			
				|  |  | import org.springframework.core.annotation.Order;
 | 
	
		
			
				|  |  | import org.springframework.expression.ExpressionParser;
 | 
	
		
			
				|  |  | import org.springframework.expression.spel.SpelEvaluationException;
 | 
	
		
			
				|  |  | import org.springframework.expression.spel.SpelParseException;
 | 
	
		
			
				|  |  | 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.ArrayList;
 | 
	
		
			
				|  |  | import java.util.Arrays;
 | 
	
		
			
				|  |  | import java.util.List;
 | 
	
		
			
				|  |  | 
 | 
	
		
			
				|  |  | /**
 | 
	
		
			
				|  |  |  * redis分布式锁AOP
 | 
	
		
			
				|  |  |  *
 | 
	
		
			
				|  |  |  * @author baichuan.wu
 | 
	
		
			
				|  |  |  * @version 1.0.0
 | 
	
		
			
				|  |  |  * @date 2019-12-26 14:16
 | 
	
		
			
				|  |  |  */
 | 
	
		
			
				|  |  | @Aspect
 | 
	
		
			
				|  |  | @Order(-1)
 | 
	
		
			
				|  |  | @Component
 | 
	
		
			
				|  |  | public class RedisLockAOP {
 | 
	
		
			
				|  |  |     private static final Logger LOGGER = LoggerFactory.getLogger(RedisLockAOP.class);
 | 
	
		
			
				|  |  |     @Autowired
 | 
	
		
			
				|  |  |     private RedisService redisService;
 | 
	
		
			
				|  |  |     private static final ExpressionParser PARSER = new SpelExpressionParser();
 | 
	
		
			
				|  |  |     private static final ParameterNameDiscoverer DISCOVERER = new LocalVariableTableParameterNameDiscoverer();
 | 
	
		
			
				|  |  |     private static final String UNDERLINE = "_";
 | 
	
		
			
				|  |  |     private static final String REDIS_LOCK = "REDIS_LOCK";
 | 
	
		
			
				|  |  | 
 | 
	
		
			
				|  |  |     /**
 | 
	
		
			
				|  |  |      * aop切入点
 | 
	
		
			
				|  |  |      *
 | 
	
		
			
				|  |  |      * @param redisLock
 | 
	
		
			
				|  |  |      */
 | 
	
		
			
				|  |  |     @Pointcut("@annotation(redisLock)")
 | 
	
		
			
				|  |  |     public void pointCut(RedisLock redisLock) {
 | 
	
		
			
				|  |  |     }
 | 
	
		
			
				|  |  | 
 | 
	
		
			
				|  |  |     /**
 | 
	
		
			
				|  |  |      * aop环绕
 | 
	
		
			
				|  |  |      *
 | 
	
		
			
				|  |  |      * @param joinPoint
 | 
	
		
			
				|  |  |      * @param redisLock
 | 
	
		
			
				|  |  |      * @return {@link Object}
 | 
	
		
			
				|  |  |      */
 | 
	
		
			
				|  |  |     @Around(value = "pointCut(redisLock)", argNames = "joinPoint,redisLock")
 | 
	
		
			
				|  |  |     public Object around(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {
 | 
	
		
			
				|  |  |         return aroundRedisLock(joinPoint, redisLock);
 | 
	
		
			
				|  |  |     }
 | 
	
		
			
				|  |  | 
 | 
	
		
			
				|  |  |     /**
 | 
	
		
			
				|  |  |      * 分布式锁
 | 
	
		
			
				|  |  |      *
 | 
	
		
			
				|  |  |      * @param joinPoint
 | 
	
		
			
				|  |  |      * @param redisLock
 | 
	
		
			
				|  |  |      * @return {@link Object}
 | 
	
		
			
				|  |  |      */
 | 
	
		
			
				|  |  |     public Object aroundRedisLock(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {
 | 
	
		
			
				|  |  |         String lockKey = null;
 | 
	
		
			
				|  |  |         Object result;
 | 
	
		
			
				|  |  |         boolean lock = false;
 | 
	
		
			
				|  |  |         try {
 | 
	
		
			
				|  |  |             lockKey = getLockKey(joinPoint, redisLock);
 | 
	
		
			
				|  |  |             lock = redisService.tryLock(lockKey, REDIS_LOCK, redisLock.time());
 | 
	
		
			
				|  |  |             if (!lock) {
 | 
	
		
			
				|  |  |                 LOGGER.error("【LOCK】{}", redisLock.msg());
 | 
	
		
			
				|  |  |                 throw new BusinessException(redisLock.msg());
 | 
	
		
			
				|  |  |             }
 | 
	
		
			
				|  |  |             result = joinPoint.proceed();
 | 
	
		
			
				|  |  |         } catch (SpelParseException e) {
 | 
	
		
			
				|  |  |             LOGGER.error("【LOCK】表达式解析异常", e);
 | 
	
		
			
				|  |  |             throw new BusinessException("【LOCK】请求异常,请稍后再试");
 | 
	
		
			
				|  |  |         } catch (SpelEvaluationException e) {
 | 
	
		
			
				|  |  |             LOGGER.error("【LOCK】表达式异常:{}", e.getMessage());
 | 
	
		
			
				|  |  |             throw new BusinessException("【LOCK】请求异常,请稍后再试");
 | 
	
		
			
				|  |  |         } catch (BusinessException e) {
 | 
	
		
			
				|  |  |             LOGGER.error("【LOCK】请求异常", e);
 | 
	
		
			
				|  |  |             throw new BusinessException(e.getMessage());
 | 
	
		
			
				|  |  |         } catch (Exception e) {
 | 
	
		
			
				|  |  |             LOGGER.error("【LOCK】请求异常", e);
 | 
	
		
			
				|  |  |             throw new BusinessException("请求异常,请稍后再试");
 | 
	
		
			
				|  |  |         } finally {
 | 
	
		
			
				|  |  |             if (lock) {
 | 
	
		
			
				|  |  |                 redisService.releaseLock(lockKey);
 | 
	
		
			
				|  |  |             }
 | 
	
		
			
				|  |  |         }
 | 
	
		
			
				|  |  |         return result;
 | 
	
		
			
				|  |  |     }
 | 
	
		
			
				|  |  | 
 | 
	
		
			
				|  |  |     /**
 | 
	
		
			
				|  |  |      * 获取分布式锁key
 | 
	
		
			
				|  |  |      *
 | 
	
		
			
				|  |  |      * @param joinPoint
 | 
	
		
			
				|  |  |      * @param redisLock
 | 
	
		
			
				|  |  |      * @return {@link String}
 | 
	
		
			
				|  |  |      */
 | 
	
		
			
				|  |  |     public String getLockKey(ProceedingJoinPoint joinPoint, RedisLock redisLock) {
 | 
	
		
			
				|  |  |         MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
 | 
	
		
			
				|  |  |         List<String> keyList = new ArrayList<>();
 | 
	
		
			
				|  |  |         keyList.add(RedisConstant.REDIS_LOCK_PREFIX);
 | 
	
		
			
				|  |  |         keyList.add(joinPoint.getTarget().getClass().getSimpleName().toUpperCase());
 | 
	
		
			
				|  |  |         // 如果配置key前缀则使用配置的,否则用方法名
 | 
	
		
			
				|  |  |         if (StringUtils.isNotBlank(redisLock.value())) {
 | 
	
		
			
				|  |  |             keyList.add(redisLock.value());
 | 
	
		
			
				|  |  |         } else {
 | 
	
		
			
				|  |  |             keyList.add(methodSignature.getName().toUpperCase());
 | 
	
		
			
				|  |  |         }
 | 
	
		
			
				|  |  |         // 解析spel表达式
 | 
	
		
			
				|  |  |         String key = getParseKey(redisLock.key(), methodSignature.getMethod(), joinPoint.getArgs());
 | 
	
		
			
				|  |  |         if (StringUtils.isNotBlank(key)) {
 | 
	
		
			
				|  |  |             keyList.add(key);
 | 
	
		
			
				|  |  |         }
 | 
	
		
			
				|  |  |         return Joiner.on(UNDERLINE).join(keyList);
 | 
	
		
			
				|  |  |     }
 | 
	
		
			
				|  |  | 
 | 
	
		
			
				|  |  |     /**
 | 
	
		
			
				|  |  |      * 获取缓存的key
 | 
	
		
			
				|  |  |      * key 定义在注解上,支持SPEL表达式
 | 
	
		
			
				|  |  |      *
 | 
	
		
			
				|  |  |      * @param key
 | 
	
		
			
				|  |  |      * @param method
 | 
	
		
			
				|  |  |      * @param args
 | 
	
		
			
				|  |  |      * @return {@link String}
 | 
	
		
			
				|  |  |      */
 | 
	
		
			
				|  |  |     public String getParseKey(String key, Method method, Object[] args) {
 | 
	
		
			
				|  |  |         if (StringUtils.isBlank(key)) {
 | 
	
		
			
				|  |  |             return getHexKey(args);
 | 
	
		
			
				|  |  |         }
 | 
	
		
			
				|  |  |         // 获取请求参数名
 | 
	
		
			
				|  |  |         String[] paramNames = DISCOVERER.getParameterNames(method);
 | 
	
		
			
				|  |  |         if (paramNames != null && paramNames.length > 0) {
 | 
	
		
			
				|  |  |             StandardEvaluationContext context = new StandardEvaluationContext();
 | 
	
		
			
				|  |  |             for (int i = 0; i < args.length; i++) {
 | 
	
		
			
				|  |  |                 context.setVariable(paramNames[i], args[i]);
 | 
	
		
			
				|  |  |             }
 | 
	
		
			
				|  |  |             // 解析表达式
 | 
	
		
			
				|  |  |             return PARSER.parseExpression(key).getValue(context, String.class);
 | 
	
		
			
				|  |  |         } else {
 | 
	
		
			
				|  |  |             return getHexKey(args);
 | 
	
		
			
				|  |  |         }
 | 
	
		
			
				|  |  |     }
 | 
	
		
			
				|  |  | 
 | 
	
		
			
				|  |  |     /**
 | 
	
		
			
				|  |  |      * hex加密key
 | 
	
		
			
				|  |  |      *
 | 
	
		
			
				|  |  |      * @param args
 | 
	
		
			
				|  |  |      * @return {@link String}
 | 
	
		
			
				|  |  |      */
 | 
	
		
			
				|  |  |     public static String getHexKey(Object[] args) {
 | 
	
		
			
				|  |  |         if (args != null && args.length > 0) {
 | 
	
		
			
				|  |  |             return DigestUtils.md5Hex(Arrays.toString(args));
 | 
	
		
			
				|  |  |         } else {
 | 
	
		
			
				|  |  |             return null;
 | 
	
		
			
				|  |  |         }
 | 
	
		
			
				|  |  |     }
 | 
	
		
			
				|  |  | }
 |