自定义注解 + AOP + Redisson:优雅实现分布式锁(增强版)

文章目录

在分布式系统中,保证数据一致性和避免并发冲突是一个常见的挑战。分布式锁是解决这一问题的有效手段。本文将介绍如何通过自定义注解AOP(面向切面编程)Redisson ,优雅地实现分布式锁,并支持注解中的SpEL表达式 ,动态生成锁的Key。同时,我们将通过PlantUML描述请求的执行流程,帮助你更好地理解整个机制。


什么是分布式锁?

分布式锁是一种在分布式系统中用于控制多个进程或线程对共享资源访问的机制。它的核心目标是:

  • 互斥性:同一时刻只有一个客户端可以持有锁。
  • 可重入性:同一个客户端可以多次获取同一把锁。
  • 高可用性:锁服务需要具备高可用性,避免单点故障。
  • 自动释放:锁需要支持超时自动释放,避免死锁。

为什么选择Redisson?

Redisson是一个基于Redis的Java客户端,提供了丰富的分布式对象和服务,包括分布式锁。它的优势在于:

  1. 简单易用:提供了简洁的API,易于集成。
  2. 高性能:基于Redis的高性能特性,支持高并发场景。
  3. 功能丰富:支持可重入锁、公平锁、读写锁等多种锁类型。
  4. 自动续期:支持锁的自动续期,避免锁过期问题。

实现思路

我们将通过以下步骤实现分布式锁:

  1. 自定义注解 :定义一个注解@DistributedLock,支持SpEL表达式动态生成锁的Key。
  2. AOP切面 :通过AOP拦截被@DistributedLock注解标记的方法,在方法执行前后加锁和释放锁。
  3. Redisson配置:集成Redisson客户端,提供分布式锁的实现。
  4. 请求执行流程:通过PlantUML描述请求的执行流程,帮助理解整个机制。

代码实现

1. 自定义注解

定义一个注解@DistributedLock,支持SpEL表达式动态生成锁的Key:

java 复制代码
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 key(); // 锁的key,支持SpEL表达式
    long waitTime() default 30; // 获取锁的最大等待时间(秒)
    long leaseTime() default 10; // 锁的持有时间(秒)
}

2. AOP切面

通过AOP拦截被@DistributedLock注解标记的方法,在方法执行前后加锁和释放锁。使用SpEL表达式解析动态Key:

java 复制代码
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
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;

@Aspect
@Component
public class DistributedLockAspect {

    @Autowired
    private RedissonClient redissonClient;

    @Around("@annotation(distributedLock)")
    public Object around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable {
        // 解析SpEL表达式,生成锁的Key
        String lockKey = parseSpEL(joinPoint, distributedLock.key());

        RLock lock = redissonClient.getLock(lockKey);

        try {
            // 尝试获取锁
            boolean isLocked = lock.tryLock(distributedLock.waitTime(), distributedLock.leaseTime(), java.util.concurrent.TimeUnit.SECONDS);
            if (!isLocked) {
                throw new RuntimeException("获取分布式锁失败");
            }

            // 执行目标方法
            return joinPoint.proceed();
        } catch (Exception e) {
            // 处理异常
            throw e;
        } finally {
            // 释放锁
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 解析SpEL表达式,生成锁的Key
     */
    private String parseSpEL(ProceedingJoinPoint joinPoint, String spEL) {
        // 获取方法参数名和值
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String[] parameterNames = signature.getParameterNames();
        Object[] args = joinPoint.getArgs();

        // 创建SpEL上下文
        StandardEvaluationContext context = new StandardEvaluationContext();
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }

        // 解析SpEL表达式
        ExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression(spEL);
        return expression.getValue(context, String.class);
    }
}

3. Redisson配置

配置Redisson客户端,连接到Redis服务器:

java 复制代码
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {

    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer()
              .setAddress("redis://127.0.0.1:6379")
              .setPassword("your_password") // 如果有密码
              .setDatabase(0);
        return Redisson.create(config);
    }
}

4. 使用示例

在业务代码中使用@DistributedLock注解,标记需要加锁的方法。通过SpEL表达式动态生成锁的Key:

java 复制代码
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    @DistributedLock(key = "'order_lock_' + #orderId", waitTime = 10, leaseTime = 5)
    public void createOrder(String orderId) {
        // 业务逻辑
        System.out.println("创建订单:" + orderId);
    }
}

请求执行流程

以下是请求的执行流程


总结

通过自定义注解、AOP和Redisson,我们可以优雅地实现分布式锁,并支持SpEL表达式动态生成锁的Key。本文详细介绍了实现思路和代码示例,并通过PlantUML描述了请求的执行流程,帮助你更好地理解整个机制。

如果你在开发中遇到类似问题,不妨尝试本文提供的解决方案,提升系统的并发处理能力!


关于作者

我是Java开发领域的专家,专注于高质量代码的设计与实现。如果你对Java技术感兴趣,欢迎关注我的博客,我们一起学习进步!

相关推荐
KIDAKN10 小时前
RabbitMQ 初步认识
分布式·rabbitmq
pan30350747910 小时前
Kafka 和 RabbitMQ的选择
分布式·kafka·rabbitmq
hzulwy13 小时前
Kafka基础理论
分布式·kafka
明达智控技术14 小时前
MR30分布式IO在全自动中药煎药机中的应用
分布式·物联网·自动化
jakeswang15 小时前
细说分布式ID
分布式
失散1316 小时前
分布式专题——1.2 Redis7核心数据结构
java·数据结构·redis·分布式·架构
王中阳Go17 小时前
头一次见问这么多kafka的问题
分布式·kafka
boonya18 小时前
Kafka核心原理与常见面试问题解析
分布式·面试·kafka
KIDAKN19 小时前
RabbitMQ 重试机制 和 TTL
分布式·rabbitmq
JAVA学习通20 小时前
【RabbitMQ】----初识 RabbitMQ
分布式·rabbitmq