Java中基于AOP注解的方式实现分布式锁

背景:

        基于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) {
        // 业务逻辑
    }
}

  • 21
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值