|
@ -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;
|
|
|
}
|
|
|
}
|
|
|
}
|