java中通过自定义注解+aop实现无侵入式分布式锁,核心是将加锁/解锁逻辑从业务代码剥离,由切面统一拦截处理:定义运行时注解支持spel动态key及超时参数,aop切面解析表达式并调用redisson等客户端加锁,确保finally释放,注意@order控制切面顺序、key唯一性及异常释放。

Java 中通过自定义注解 + AOP 实现无侵入式分布式锁,核心是把加锁/解锁逻辑从业务代码中剥离,由切面统一拦截处理。关键在于注解声明锁参数、AOP 拦截方法执行、在切点前后调用分布式锁组件(如 Redisson、RedisTemplate 或 ZooKeeper 客户端)。
定义可配置的分布式锁注解
创建一个运行时保留、可作用于方法的注解,支持指定锁的 key 表达式、超时时间、等待时间等:
- 使用 @Target(ElementType.METHOD) 和 @Retention(RetentionPolicy.RUNTIME)
- key 支持 SpEL 表达式(如 "#id" 或 "#user.username"),便于动态生成唯一锁标识
- 提供 lockTimeout(锁自动释放时间)、waitTime(获取锁最大等待时间)等常用参数,默认值要合理(如 10 秒和 3 秒)
编写 AOP 切面实现锁的自动加/解锁
切面需在目标方法执行前尝试获取锁,成功后才放行;方法结束后或异常时确保释放锁:
- 使用 @Around 切入带自定义注解的方法,获取注解元数据和方法参数
- 解析 SpEL 表达式生成实际 lockKey(借助 ExpressionParser 和 EvaluationContext)
- 调用分布式锁客户端(如 Redisson 的 RLock.tryLock(waitTime, lockTimeout, TimeUnit.SECONDS))
- 加锁失败直接抛出异常(如 LockAcquireException),避免业务逻辑执行;加锁成功则 proceed,finally 块中释放锁
集成可靠的分布式锁实现
不建议手写 Redis SETNX + Lua 脚本,优先选用成熟客户端:
- Redisson 提供开箱即用的可重入锁、看门狗机制(自动续期),适配 Spring Boot 自动装配
- 若用原生 RedisTemplate,需封装原子性加锁脚本(含 UUID 校验防误删)和解锁 Lua 脚本
- 锁粒度建议按业务场景控制:方法级锁适合幂等操作;细粒度锁(如订单 ID)更利于并发吞吐
注意事项与避坑点
看似简单,但几个细节决定是否真正“无侵入”和稳定可用:
- 切面顺序:若方法上同时有 @Transactional,需确保锁切面在事务切面外层(用 @Order 控制),否则可能锁住未提交数据
- 锁 key 唯一性:避免硬编码 key,必须结合参数动态生成,防止不同请求误共享同一把锁
- 非幂等方法慎用:锁只保证单次执行,不能替代业务层幂等设计(如重复支付)
- 异常路径释放:务必在 finally 或 @AfterThrowing 中释放锁,防止线程阻塞或锁泄露
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











