如何使用AOP设计一个分布式锁注解?
1、在pom.xml中配置依赖
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.26</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
        </dependency>2、逻辑代码
创建一下多个文件夹,并复制粘贴进代码
2.1、RedissonConfig.java
需要配置一下Redisson
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;
/**
 * Redisson 配置
 *
 */
@Configuration
public class RedissonConfig {
    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer()
                .setAddress("redis://192.168.57.111:6379") // Redis地址
                .setPassword(null) // 如果没有密码,设置为null
                .setDatabase(0); // 使用的Redis数据库索引
        RedissonClient redissonClient = Redisson.create(config);
        // 测试连接
        try {
            redissonClient.getKeys().count();
            System.out.println("Redisson connected to Redis successfully.");
        } catch (Exception e) {
            System.err.println("Failed to connect to Redis: " + e.getMessage());
        }
        return redissonClient;
    }
}需要修改setAddress("redis://192.168.57.111:6379")中的地址为自己的redis地址
2.2、DistributedLock.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 prefix() default "lock:"; // 锁前缀,默认为 "lock:"
    String key() default ""; // 锁的Key,支持SpEL表达式
    long leaseTime() default 30; // 锁的默认持有时间,单位秒
    long waitTime() default 10; // 获取锁的等待时间,单位秒
}
2.3、DistributedLockAspect.java
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeUnit;
@Aspect
@Component
public class DistributedLockAspect {
    @Autowired
    private RedissonClient redissonClient; // 注入 Redisson 客户端
    /**
     * 定义切入点,匹配所有使用 @DistributedLock 注解的方法
     * @param distributedLock 分布式锁注解对象
     */
    @Pointcut("@annotation(distributedLock)")
    public void distributedLockPointcut(DistributedLock distributedLock) {}
    /**
     * 环绕通知:在目标方法执行前后处理分布式锁逻辑
     *
     * @param joinPoint 切点,表示目标方法的执行点
     * @param distributedLock 注解,用于获取注解属性值
     * @return 目标方法的返回值
     * @throws Throwable 当目标方法抛出异常时向上抛出
     */
    @Around("distributedLockPointcut(distributedLock)")
    public Object around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable {
        String lockKey = buildLockKey(distributedLock.prefix(),distributedLock.key(), joinPoint); // 生成锁Key
        RLock lock = redissonClient.getLock(lockKey);
        boolean isLocked = false;
        try {
            while (true) {
                // 尝试获取锁
                isLocked = lock.tryLock(distributedLock.waitTime(), distributedLock.leaseTime(), TimeUnit.SECONDS);
                if (isLocked) {
                    System.out.println(Thread.currentThread().getName() + " 成功获取锁,key: " + lockKey);
                    return joinPoint.proceed(); // 执行目标方法
                } else {
                    System.out.println(Thread.currentThread().getName() + " 未获取到锁,key: " + lockKey + ",重试中...");
                    // 等待一段时间后再重试
                    Thread.sleep(500);
                }
            }
        } finally {
            if (isLocked && lock.isHeldByCurrentThread()) {
                lock.unlock(); // 释放锁
                System.out.println(Thread.currentThread().getName() + " 已释放锁,key: " + lockKey);
            }
        }
    }
    private String buildLockKey(String prefix, String key, ProceedingJoinPoint joinPoint) {
        if (key.isEmpty()) {
            throw new IllegalArgumentException("Lock key cannot be empty");
        }
        // 拼接锁前缀和原始键
        String rawKey = prefix + key;
        // 将拼接后的键通过 MD5 生成唯一的锁键
        return generateMD5(rawKey);
    }
    /**
     * 使用 MD5 生成锁键
     * @param input 原始键
     * @return MD5 生成的锁键
     */
    private String generateMD5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(input.getBytes());
            // 转换为 32 位十六进制字符串
            StringBuilder hexString = new StringBuilder();
            for (byte b : digest) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Failed to generate MD5 hash", e);
        }
    }
}
这三步做完之后,该注解就能用了。
3、业务模拟测试
根据以下创建文件,并写入代码
3.1、InventoryService.java
这里测试可以itemId为键加锁
import com.pshao.charplatform.utils.distributedLock.DistributedLock;
import org.springframework.stereotype.Service;
@Service
public class InventoryService {
    @DistributedLock(prefix = "stock",key = "#itemId", leaseTime = 10, waitTime = 1)
    public void reduceStock(Long itemId, int quantity) {
        System.out.println(Thread.currentThread().getName() + " 正在处理库存扣减: itemId=" + itemId + ", quantity=" + quantity);
        try {
            Thread.sleep(2000); // 模拟耗时操作,修改为两秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " 完成库存扣减: itemId=" + itemId);
    }
}这里可以输入以下多个参数,key是必须的,其他可以不输入
String prefix() default "lock:"; // 锁前缀,默认为 "lock:"
 String key() default ""; // 锁的Key
 long leaseTime() default 30; // 锁的默认持有时间,单位秒
 long waitTime() default 10; // 获取锁的等待时间,单位秒
3.2、DistributedLockApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@SpringBootApplication(scanBasePackages = "com.pshao.charplatform")
public class DistributedLockApplication {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(DistributedLockApplication.class, args);
        InventoryService inventoryService = context.getBean(InventoryService.class);
        ExecutorService executor = Executors.newFixedThreadPool(3); // 创建3个线程
        // 模拟多个线程竞争同一资源
        for (int i = 0; i < 3; i++) {
            executor.submit(() -> inventoryService.reduceStock(123L, 10));
        }
        executor.shutdown();
    }
}
3.3、运行查看测试结果

测试成功!
如果运行后出现,类似以下日志
Action: Correct the classpath of your application so that it contains compatible versions of the classes org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator and org.springframework.util.ClassUtils
考虑是版本冲突,可以参考Correct the classpath of your application so that it contains compatible versions......版本不兼容解决方法-CSDN博客



















