目录
1、annotation
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DLock {
/**
* 锁字符串值 可多个锁 顺序取
* @return
*/
String[] value();
/**
* 等待锁时长 秒
* @return
*/
int timeOut() default 30;
/**
* 锁定时长 秒
* @return
*/
int lockTime() default 60;
}
2、切面aspect
import com.lw.code.auth.annotation.DLock;
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.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
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.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Slf4j
@Order(1)
@Aspect
@Component
public class DLockAspect {
private final static String LOCK_PREFIX = "DLOCK:";
/** 方法参数名解析 */
private final static LocalVariableTableParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
@Resource
private RedissonClient redissonClient;
@Around(value = "@annotation(dLock)", argNames = "proceedingJoinPoint,dLock")
public Object around(ProceedingJoinPoint proceedingJoinPoint, DLock dLock) throws Throwable {
return doLock(proceedingJoinPoint, dLock);
}
private Object doLock(ProceedingJoinPoint proceedingJoinPoint, DLock dLock) throws Throwable {
/* 获取拦截方法 参数 */
Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
Object[] params = proceedingJoinPoint.getArgs();
/* 创建上下文 */
String[] paraNameArray = parameterNameDiscoverer.getParameterNames(method);
StandardEvaluationContext context = new StandardEvaluationContext();
if (paraNameArray != null) {
for (int i = 0; i < paraNameArray.length; i++) {
context.setVariable(paraNameArray[i], params[i]);
}
}
/* 创建解析器 */
ExpressionParser parser = new SpelExpressionParser();
/* 解析锁 */
List<RLock> rLockList = new ArrayList<>(dLock.value().length);
for (String oriLockString : dLock.value()) {
String lockString = parser.parseExpression(oriLockString, new TemplateParserContext()).getValue(context, String.class);
rLockList.add(redissonClient.getFairLock(LOCK_PREFIX + lockString));
}
/* 加锁数量 */
int lockNumber = 0;
try {
/* 尝试加锁 */
for (RLock lock : rLockList) {
boolean isLock = false;
try {
isLock = lock.tryLock(dLock.timeOut(), dLock.lockTime(), TimeUnit.SECONDS);
} catch (InterruptedException ie) {
log.error(ie.getMessage(), ie);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
/* 加锁失败 */
if (!isLock) {
throw new RuntimeException("分布式锁加锁失败");
}
lockNumber++;
}
/* 执行业务 */
return proceedingJoinPoint.proceed();
} finally {
/* 解锁 */
if (lockNumber > 0) {
/* 解锁数量 */
int unLockNumber = 0;
for (RLock lock: rLockList) {
try {
lock.unlock();
} catch (IllegalMonitorStateException imse) {
log.error(imse.getMessage(), imse);
// "警告 - 分布式锁:%s, 发生多持锁现象", lock.getName();
} catch (Exception e) {
log.error(e.getMessage(), e);
// 未知锁异常
}
unLockNumber++;
/* 解锁到未加锁部分 */
if (unLockNumber >= lockNumber) {
break;
}
}
}
}
}
}
3、使用
@DLock(DLockConstants.ORDER_PAY+ "#{#orderId}")