背景:
基于AOP的方式来实现分布式锁,主要是为了简化锁的开发,也可以方式事务的粒度大于锁的粒度, 导致出现数据不一致的问题,也能使得代码更加优雅规范,话不多说咱们上代码
1.第一步 导入pom依赖,根据自己项目实际情况来
<dependencies>
<!-- Redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.13.4</version>
</dependency>
<!-- Spring AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
2.第二步 自定义注解
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
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)
public @interface DistributedLock {
String lockKey() default ""; // 锁的键名
String expireTimeExpression() default ""; // SpEL表达式,用于计算锁的过期时间(单位:秒)
}
3.第三步,自定义切面
// ...之前的导入保持不变
@Aspect
@Component
public class DistributedLockAspect {
@Autowired
private RedissonClient redissonClient;
@Autowired
private ParameterNameDiscoverer parameterNameDiscoverer;
private final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
@Around("@annotation(distributedLock)")
public Object doAround(ProceedingJoinPoint pjp, DistributedLock distributedLock) throws Throwable {
String lockKey = evaluateExpression(distributedLock.expireTimeExpression(), pjp);
long expireTimeInSeconds = calculateExpireTime(distributedLock.expireTimeExpression(), pjp);
if (lockKey == null || lockKey.isEmpty()) {
throw new IllegalArgumentException("Generated lock key cannot be empty.");
}
RLock lock = redissonClient.getLock(lockKey);
boolean isLocked = false;
try {
isLocked = lock.tryLock(expireTimeInSeconds, TimeUnit.SECONDS);
if (isLocked) {
return pjp.proceed();
} else {
throw new RuntimeException("获取锁失败");
}
} catch (Throwable e) {
throw e;
} finally {
if (isLocked) {
lock.unlock();
}
}
}
/**
* 根据SpEL表达式计算锁的过期时间。
*/
private long calculateExpireTime(String expression, ProceedingJoinPoint pjp) {
if (expression.isEmpty()) {
throw new IllegalArgumentException("expireTimeExpression cannot be empty.");
}
return evaluateLongExpression(expression, pjp);
}
/**
* 解析SpEL表达式得到锁的键名或过期时间(字符串或数值)。
*/
private String evaluateExpression(String expression, ProceedingJoinPoint pjp) {
if (expression.isEmpty()) {
return null;
}
return spelExpressionParser.parseExpression(expression).getValue(getEvaluationContext(pjp), String.class);
}
/**
* 解析SpEL表达式得到数值结果。
*/
private long evaluateLongExpression(String expression, ProceedingJoinPoint pjp) {
return spelExpressionParser.parseExpression(expression).getValue(getEvaluationContext(pjp), Long.class);
}
/**
* 创建用于SpEL表达式求值的上下文。
*/
private EvaluationContext getEvaluationContext(ProceedingJoinPoint pjp) {
Method method = ((MethodSignature) pjp.getSignature()).getMethod();
Object[] args = pjp.getArgs();
return new MethodBasedEvaluationContext(null, method, args, parameterNameDiscoverer);
}
}
4.代码示例
@Service
public class OrderService {
@DistributedLock(lockKeyExpression = "#{order.orderId}", expireTimeExpression = "#{60 + order.timeoutSeconds}")
public void processOrder(Order order) {
// 业务逻辑
}
}