1.编写注解类
import org.springframework.core.annotation.AliasFor;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisLock {
/**
* 锁的前缀
* @return
*/
String title() default "yuhu-erp";
/**
* 锁的key
* @return
*/
@AliasFor(value = "value")
String key() default "lock";
@AliasFor(value = "key")
String value() default "lock";
/**
* 锁类型
* @return
*/
// RedisLockType lockType() default RedisLockType.READ_WRITE_LOCK;
/**
* 等待时间
* @return
*/
long waitTime() default 3L;
/**
* 续期时间
* @return
*/
long leaseTime() default 15L;
/**
* 时间单位
* @return
*/
TimeUnit timeUnit() default TimeUnit.SECONDS;
/**
* 是否自动续期
* @return
*/
boolean autoRenew() default true;
/**
* 错误提示
* @return
*/
String message() default "加锁失败";
}
2.编写注解AOP处理类
import com.yuhucoldchain.erp.annotation.RedisLock;
import com.yuhucoldchain.erp.exception.BusinessRunTimeException;
import com.yuhucoldchain.erp.utils.SystemCodeEnum;
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.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Component
@Aspect
public class RedisLockAspectj {
@Autowired
private RedissonClient redissonClient;
@Pointcut(value = "@annotation(com.yuhucoldchain.erp.annotation.RedisLock)")
public void lockPoint() {
}
@Around("lockPoint()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Method method = methodSignature.getMethod();
if (method.getDeclaringClass().isInterface()) {
method = joinPoint.getTarget().getClass().getDeclaredMethod(joinPoint.getSignature().getName(),
method.getParameterTypes());
}
RedisLock redisLock = AnnotationUtils.findAnnotation(method, RedisLock.class);
if (redisLock == null) {
return joinPoint.proceed();
}
String keySource = redisLock.key();
String key = keySource;
if (keySource.startsWith("$") || keySource.startsWith("#") || keySource.startsWith("@")) {
// 上下文默认是切点
EvaluationContext context = new StandardEvaluationContext(joinPoint);
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(keySource);
key = exp.getValue(context, String.class);
}
String title = redisLock.title();
String k = title + "-" + key;
RLock lock = redissonClient.getLock(k);
boolean isLocked = false;
try {
// 尝试在指定waitTime时间内获取锁
isLocked = lock.tryLock(redisLock.waitTime(), redisLock.leaseTime(), redisLock.timeUnit());
if (isLocked) {
return joinPoint.proceed();
} else {
// 获取锁失败,根据业务抛出自定义异常
throw new BusinessRunTimeException(SystemCodeEnum.SYSTEM_ERROR.getCode(), redisLock.message());
}
} finally {
// 如果当前线程持有锁,则释放锁
if (isLocked) {
lock.unlock();
}
}
}
}
3.方法上如何使用
(1)固定的常量key
@RedisLock(BusinessConstants.LOCK_UPDATE_FILENAME)
(2)方法请求的变量做为key
@RedisLock(title = BusinessConstants.LOCK_REDEMPTION_CONTROL, key = "#root.args[0]")
public void redemptionControl(String number, String type)
(3)如果需要逻辑判断来决定具体的key,可通过新增方法来控制
public String getKey(OrderDataReq req) {
if(req.getSource().equals(BusinessConstants.SOURCE_LOAN)){
return req.getInfo().getApplyNo();
}else {
return req.getInfo().getNumber();
}
}
@RedisLock(title = BusinessConstants.LOCK_LOAN_PURCHASE_ADD, key ="#root.target.getKey(#root.args[0])")
public String insertPurchaseDepotHead(OrderDataReq req){
}