当前位置: 首页 > news >正文

lock4j 库中的 @Lock4j 注解进行全面的概述和深度的源码级剖析。

首先需要明确:@Lock44j 并非 Spring 官方提供的注解,它是国内开源社区(主要来自 baomidou 团队)提供的一个分布式锁注解框架,通常与 lock4j 库一起使用。它的设计理念是让开发者通过一个简单的注解,就能优雅地实现分布式锁逻辑,其底层可以适配不同的锁实现(如 Redis, Redisson, Zookeeper 等)。


一、@Lock4j 详细概述

1. 核心功能

@Lock4j 的核心功能是声明式分布式锁。它通过 AOP 在方法执行前尝试获取锁,执行后自动释放锁,将分布式锁的通用逻辑与业务代码完全解耦。

2. 核心注解属性

@Lock4j 注解通常包含以下属性,用于精细控制锁的行为:

属性类型默认值描述
nameString""锁的名称。与 key 共同组成最终的锁标识符。
keyString""锁的键。支持 Spring EL (SpEL) 表达式,用于从方法参数中动态获取值,实现更细粒度的锁(如 #user.id)。namekey 用冒号连接形成最终锁键。
keysString[]{}锁的键数组。同样支持 SpEL,多个键会被拼接起来形成最终锁键。
expirelong30000锁的自动释放时间(毫秒)。防止因服务宕机导致死锁。
acquireTimeoutlong10000获取锁的最大等待时间(毫秒)。超过这个时间还未获取到锁则抛出异常。
executorClass<? extends LockExecutor>LockExecutor.class锁执行器。用于指定获取和释放锁的具体实现(如 Redis、Zookeeper)。
onLockFailureLockFailureStrategyThrowException获取锁失败时的处理策略。例如:快速失败抛出异常、忽略并继续执行、重试等。
3. 工作流程

在使用时v,引入starter依赖,源码如下所示:

        <dependency><groupId>com.baomidou</groupId><artifactId>lock4j-redis-template-spring-boot-starter</artifactId><version>2.2.7</version></dependency>

@Lock4j`注解的工作流程如下图所示:

在这里插入图片描述

二、实现源码剖析

我们以 lock4j 库的典型实现为例进行剖析。其核心架构分为三层:注解层切面层执行器层

1. 注解定义 (org.springblade.core.lock.annotation.Lock4j)

这是注解的声明,非常简单。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lock4j {String name() default "";String key() default "";String[] keys() default {};long expire() default 30000;long acquireTimeout() default 10000;Class<? extends LockExecutor> executor() default LockExecutor.class;LockFailureStrategy onLockFailure() default LockFailureStrategy.THROW_EXCEPTION;
}
// 失败策略枚举
public enum LockFailureStrategy {THROW_EXCEPTION, // 抛出异常IGNORE,          // 忽略,继续执行方法CONTINUE         // 忽略,直接返回(不执行方法)
}
2. 切面层 (LockAspect) - 核心中的核心

这是整个功能的大脑,负责协调整个流程。

@Aspect
@AllArgsConstructor // Lombok,注入依赖
public class LockAspect {// 锁模板,提供了加锁/解锁的通用高级APIprivate final LockTemplate lockTemplate;// SpEL表达式解析器private final ExpressionParser expressionParser = new SpelExpressionParser();// 参数名发现器,用于解析SpEL中的#p0, #a0, #user等参数名private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();@Around(value = "@annotation(lock4j)")public Object around(ProceedingJoinPoint point, Lock4j lock4j) throws Throwable {// 1. 动态解析SpEL,生成最终的锁KeyString lockKey = getLockKey(point, lock4j);// 2. 根据注解配置,获取对应的锁执行器(如RedisExecutor)LockExecutor lockExecutor = LockExecutorFactory.getExecutor(lock4j.executor());// 3. 构建锁信息对象(封装了key, expire, acquireTimeout等)LockInfo lockInfo = LockInfo.builder().lockKey(lockKey).leaseTime(lock4j.expire()).acquireTimeout(lock4j.acquireTimeout()).build();// 4. 尝试获取锁boolean acquired = lockTemplate.acquire(lockInfo, lockExecutor, lock4j.acquireTimeout());// 5. 根据获取结果和失败策略进行处理if (!acquired) {return handleLockFailure(point, lock4j.onLockFailure(), lockInfo);}// 6. 获取锁成功,执行原方法,并在finally块中释放锁try {return point.proceed();} finally {lockTemplate.release(lockInfo, lockExecutor);}}/*** 核心方法:解析SpEL,拼接最终LockKey*/private String getLockKey(ProceedingJoinPoint point, Lock4j lock4j) {// 获取方法签名和参数MethodSignature signature = (MethodSignature) point.getSignature();Object[] args = point.getArgs();Method method = signature.getMethod();// 创建SpEL上下文EvaluationContext context = new MethodBasedEvaluationContext(null, method, args, parameterNameDiscoverer);// 解析 name 部分String namePart = lock4j.name();// 解析 keys 部分(支持多个key)List<String> keyParts = new ArrayList<>();if (ArrayUtil.isNotEmpty(lock4j.keys())) {for (String keyExpr : lock4j.keys()) {if (StringUtil.isNotBlank(keyExpr)) {String value = expressionParser.parseExpression(keyExpr).getValue(context, String.class);keyParts.add(value);}}}// 解析单个 key (兼容旧版)else if (StringUtil.isNotBlank(lock4j.key())) {String value = expressionParser.parseExpression(lock4j.key()).getValue(context, String.class);keyParts.add(value);}// 拼接最终的LockKey: name:keyPart1:keyPart2:...return Stream.of(Collections.singletonList(namePart), keyParts).flatMap(Collection::stream).filter(StringUtil::isNotBlank).collect(Collectors.joining(":"));}// 处理获取锁失败的逻辑private Object handleLockFailure(/*...*/) { /*...*/ }
}
3. 执行器层 (LockExecutor) - 底层实现

这是一个抽象接口,定义了锁操作的基本契约,不同的实现类对接不同的中间件。

public interface LockExecutor {// 尝试获取锁boolean acquire(LockInfo lockInfo);// 释放锁void release(LockInfo lockInfo);
}// Redis 实现 (基于 Lua 脚本保证原子性)
public class RedisLockExecutor implements LockExecutor {private final StringRedisTemplate stringRedisTemplate;// 加锁 Lua 脚本: SET lock_key unique_value NX PX expire_timeprivate static final String LOCK_SCRIPT = "if redis.call('set', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2]) then return 1 else return 0 end";// 解锁 Lua 脚本: 只有锁的值匹配才删除,防止误删private static final String UNLOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";@Overridepublic boolean acquire(LockInfo lockInfo) {// 生成一个唯一值(UUID),用于标识加锁的客户端,防止误删String lockValue = UUID.randomUUID().toString();lockInfo.setLockValue(lockValue);Long result = stringRedisTemplate.execute(new DefaultRedisScript<>(LOCK_SCRIPT, Long.class),Collections.singletonList(lockInfo.getLockKey()),lockValue,String.valueOf(lockInfo.getLeaseTime()));return result != null && result == 1;}@Overridepublic void release(LockInfo lockInfo) {stringRedisTemplate.execute(new DefaultRedisScript<>(UNLOCK_SCRIPT, Long.class),Collections.singletonList(lockInfo.getLockKey()),lockInfo.getLockValue() // 传入唯一值进行校验);}
}// Redisson 实现 (更简单,利用其原生API)
public class RedissonLockExecutor implements LockExecutor {private final RedissonClient redissonClient;@Overridepublic boolean acquire(LockInfo lockInfo) {RLock lock = redissonClient.getLock(lockInfo.getLockKey());try {return lock.tryLock(lockInfo.getAcquireTimeout(), lockInfo.getLeaseTime(), TimeUnit.MILLISECONDS);} catch (InterruptedException e) {Thread.currentThread().interrupt();return false;}}@Overridepublic void release(LockInfo lockInfo) {RLock lock = redissonClient.getLock(lockInfo.getLockKey());if (lock.isHeldByCurrentThread()) {lock.unlock();}}
}
4. 模板层 (LockTemplate) - 工具层

提供一些模板方法,对执行器的调用进行封装,可能加入一些重试逻辑或日志。

public class LockTemplate {public boolean acquire(LockInfo lockInfo, LockExecutor executor, long acquireTimeout) {// 可能会在此处实现重试机制long endTime = System.currentTimeMillis() + acquireTimeout;while (System.currentTimeMillis() < endTime) {if (executor.acquire(lockInfo)) {return true;}try {Thread.sleep(100); // 短暂休眠后重试} catch (InterruptedException e) {Thread.currentThread().interrupt();break;}}return false;}public void release(LockInfo lockInfo, LockExecutor executor) {executor.release(lockInfo);}
}

三、总结与最佳实践

总结:
@Lock4j 是一个基于 Spring AOP 和 SpEL 的强大声明式分布式锁框架。它通过注解驱动执行器插件化Key动态化的设计,将复杂的分布式锁技术细节完全隐藏,让开发者能够专注于业务逻辑。

最佳实践:

  1. 锁粒度:使用 keykeys 属性设置细粒度锁,避免使用粗粒度的全局锁,提高并发性能。例如:@Lock4j(name = "order", key = "#id")
  2. 超时设置expire 时间应大于业务方法的平均执行时间,但不宜过长。acquireTimeout 根据业务容忍度设置。
  3. 失败策略:根据业务场景选择合适的 onLockFailure 策略。支付等关键业务应 THROW_EXCEPTION,一些统计更新场景可以 IGNORE
  4. 监控:对加锁失败(LockFailureException)进行监控和告警,及时发现系统瓶颈或问题。
  5. 选择执行器:生产环境推荐使用 RedissonLockExecutor,因为它提供了看门狗自动续期、可重入等高级特性,更为可靠。
http://www.lryc.cn/news/627064.html

相关文章:

  • Ubuntu实现程序开机自动运行
  • Windows 如何清理右键菜单?电脑桌面右键菜单里出现一个清理内存 怎么去掉?
  • 强制重启导致Ubuntu24.04LTS amd的WIFI无法使用的解决方案
  • 电芯自动分选装盒机:高效整合分选与包装的智能化解决方案
  • unfold 切图像,图形transformer的切割操作
  • 【蒸蒸日上】军八武将篇——标1
  • 仿真驱动的AI自动驾驶汽车安全设计与测试
  • 30.Linux cobbler自动化部署
  • 计算机网络基础复习
  • 对象存储 COS 端到端质量系列——终端 COS SDK 网络优化
  • 【自记】Power BI 中 ALL、ALLSELECTED、ALLEXCEPT、ALLNOBLANKROW 的区别说明
  • Python打卡Day47 注意力热图可视化
  • 分享一个基于spark大数据的海洋塑料污染监测与可视化分析系统 基于Python的海洋塑料污染数据交互式可视化分析系统
  • Qwen Image edit的ComfyUI工作流搭建
  • 2025招商铸盾车联网CTF竞赛初赛题解
  • IT运维背锅权限泄露?集中式管控如何化解风险?
  • Docker Compose命令一览(Docker Compose指令、docker-compose命令)
  • Javascript面试题及详细答案150道之(121-135)
  • Linux Capability 解析
  • 达梦数据库-实时主备集群部署详解(附图文)手工搭建一主一备数据守护集群DW
  • Java 通过 m3u8 链接下载所有 ts 视频切片并合并转换为 mp4 格式
  • 齐次线性方程组最小二乘解
  • 接美国血统(中序、后序→前序)
  • 【网络运维】Linux:正则表达式
  • 虚幻引擎目录结构
  • AGV小车cad+三维图+设计说明书
  • C++ 力扣 438.找到字符串中所有字母异位词 题解 优选算法 滑动窗口 每日一题
  • Java 线程池ThreadPoolExecutor源码解读
  • 服务器内存条不识别及服务器内存位置图
  • linux的sysctl系统以及systemd系统。