一个基于 Redisson 实现分布式锁的库存 AOP 切面处理逻辑

一个基于 Redisson 实现分布式锁的库存扣减场景,包含了具体的业务接口和对应的 AOP 切面处理逻辑

🛒 库存扣减业务接口

该接口演示了如何利用自定义注解 @RedissonLock 实现分布式锁,并包含库存检查、模拟耗时操作、幂等性校验及库存扣减逻辑。

/**
库存扣减
@param stockDeductDto
@return
*/
@PostMapping(value = "/test/deduct/stock")
@ResponseBody
@RedissonLock(key = "'lock:' + #stockDeductDto.transactionId", fairLock = true)
public Result deductStock(@RequestBody StockDeductDto stockDeductDto) {

int deductStock = stockDeductDto.getDeductStock();

// 1. 库存预检查
if (deductStock > inventory.get()) {
    return Result.failure("库存不足");
}

// 2. 模拟业务处理耗时 (等待5s)
try {
    Thread.sleep(5000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

// 3. 幂等性判断:检查 transactionMap 是否已存在 transactionId
Integer status = trxMap.get(stockDeductDto.getTransactionId());

// 状态 2:已回滚处理
if (status != null && status == 2) {
    return Result.successWithErrorCode(
        ResultStatus.Already_Processed.code + "",
        ResultStatus.Already_Processed.name, 
        "已回滚处理,无法再操作"
    );
}

// 状态 1:已处理
if (status != null && status == 1) {
    return Result.successWithErrorCode(
        ResultStatus.Already_Processed.code + "",
        ResultStatus.Already_Processed.name, 
        "已处理,无法再操作"
    );
}

// 4. 执行扣减
inventory.addAndGet(-deductStock);
trxMap.put(stockDeductDto.getTransactionId(), 1);

log.info("库存扣减成功,TransactionId:{}", stockDeductDto.getTransactionId());
return Result.success();

}

🔒 Redisson 分布式锁切面实现

该切面类负责拦截带有 @RedissonLock 注解的方法,解析 SpEL 表达式生成锁键,并处理锁的获取(包括公平锁和等待超时机制)与释放。

import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Slf4j
public class RedissonLockAspect {

@Autowired
private RedissonLockerUtil redissonLockerUtil;

/**
 环绕通知:处理分布式锁逻辑
 */
@Around("@annotation(redissonLock)")
public Object around(ProceedingJoinPoint joinPoint, RedissonLock redissonLock) throws Throwable {
    String lockKey = parseSpel(redissonLock.key(), joinPoint); // 解析 SpEL 表达式
    long waitTime = redissonLock.waitTime();
    long leaseTime = redissonLock.leaseTime();
    boolean fairLock = redissonLock.fairLock();
    RLock lock = null;
    
    try {
        // 尝试获取锁逻辑
        if (waitTime > 0) {
            // 带超时时间的尝试获取锁
            lock = redissonLockerUtil.tryLock(lockKey, TimeUnit.SECONDS, waitTime, leaseTime);
            if (lock == null) {
                return Result.failure("系统繁忙,请稍后再试");
            }
        } else {
            // 无限等待排队锁 (公平锁/非公平锁)
            log.info("尝试获取无限等待排队锁,key: {}", lockKey);
            lock = fairLock ? redissonLockerUtil.fairlock(lockKey) : redissonLockerUtil.lock(lockKey);
            if (lock == null) {
                log.warn("获取锁失败: {}", lockKey);
                return Result.failure("系统繁忙,请稍后再试");
            }
        }
        
        log.info("成功获取锁: {}", lockKey);

        // 执行业务逻辑
        return joinPoint.proceed();
        
    } finally {
        // 释放锁
        if (lock != null) {
            redissonLockerUtil.unlock(lock);
            log.info("锁已释放: {}", lockKey);
        }
    }
}

/**
 解析 SpEL 表达式
 */
private String parseSpel(String spel, ProceedingJoinPoint joinPoint) {
    // 获取方法参数名和值
    MethodSignature signature = (MethodSignature) joinPoint.getSignature();
    String[] parameterNames = signature.getParameterNames();
    Object[] args = joinPoint.getArgs();

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

    // 解析并获取表达式值
    ExpressionParser parser = new SpelExpressionParser();
    return parser.parseExpression(spel).getValue(context, String.class);
}

}

posted @ 2026-04-12 22:47  cqs1234  阅读(5)  评论(0)    收藏  举报