Lock4j的锁代理浅析
一、背景
工作项目中使用baomidou
的lock4j
来进行分布式锁相关处理。在使用中还是出现了一些状况, 不得不稍微理解下lock4j
中的一些基本设计思路和方式。
二、基本知识
在Spring
中使用AOP
实际上有好几种方式: 通过@AspectJ注解方式、通过xml
配置文件方式、通过实现开放的一些抽象接口或者具体的实现方式。不论是使用哪种方式, 都需要指定出AOP
中一些必备的结构。
这里简单描述下一些必需创建的结构:
- 增强(
Advice
): 很多地方称为通知, 但实际上理解为增强更为准确. 用于表示具体的增强逻辑, 这里以日志前置增强为例, 前置增强顾名思义就是在某个逻辑之前添加保存日志的增强逻辑, 这里的日志保存逻辑即前置增强需要处理的逻辑, 所以前置增强的逻辑实际上就是一段处理逻辑代码, 和具体的目标逻辑之间没有关系. - 切入点(
Pointcut
): 用于表示具体要切入的地方, 以上述前置增强为例, 在编写好日志增强的逻辑代码之后, 要应用到多少个地方, 也就是多少个地方需要添加这个日志前置增强, 这里就需要由切入点来标记具体需要切入的地方。 - 切面(
Aspect
): 切面可以简单理解为增强Advice
和切入点Pointcut
的组合. - 目标对象(
Target Object
): 用于表示具体被增强的对象, 因为在Spring
中是通过动态代理来实现切面处理, 故被增强对象就是目标对象. - 代理对象(
AOP Proxy
): 用于表示将切面应用到目标对象而创建的代理对象。
三、浅析
在lock4j
项目中, 大致的锁代理机制为创建一个基于LockInterceptor
环绕增强的Advice
和@Lock4j
注解标记的切点Pointcut
的LockAnnotationAdvisor
切面。
环绕增强
Advice
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38public class LockInterceptor implements MethodInterceptor {
private final LockTemplate lockTemplate;
public LockInterceptor(LockTemplate lockTemplate) {
this.lockTemplate = lockTemplate;
}
/**
* 具体的环绕增强逻辑
*
* @Author: xiaocainiaoya
* @Date: 2021/12/29 11:31:23
* @param invocation
* @return:
**/
public Object invoke(MethodInvocation invocation) throws Throwable {
LockInfo lockInfo = null;
try {
Lock4j lock4j = invocation.getMethod().getAnnotation(Lock4j.class);
Lock4jConfig lock4JConfig = Lock4jConfig.builder().client(lock4j.client()).type(lock4j.type())
.keys(lock4j.keys()).lockKeyBuilder(lock4j.keyBuilder()).acquireTimeout(lock4j.acquireTimeout())
.expire(lock4j.expire()).lockFailureStrategy(lock4j.lockFailureStrategy()).build();
LockTemplate lockTemplateProxy = (LockTemplate) new Lock4jProxyFactory(lockTemplate).getProxyInstance();
lockInfo = lockTemplateProxy.lock(invocation, lock4JConfig);
if (null != lockInfo) {
return invocation.proceed();
}
return null;
} finally {
if (null != lockInfo) {
log.debug("[@Lock4j] releaseLock , current lockKey = {} ",lockInfo.getLockKey());
lockTemplate.releaseLock(lockInfo);
}
}
}
}切点
Pointcut
:1
2
3
4/**
* 标记在方法上标记Lock4j注解的方法即为此aop的切点
*/
private final Pointcut pointcut = AnnotationMatchingPointcut.forMethodAnnotation(Lock4j.class);切面
LockAnnotationAdvisor
1 | public class LockAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware { |
以上就实现了若在方法上标记了@Lock4j
注解就会进入到LockInterceptor
的增强逻辑上进行具体的加锁逻辑。
所以此实现采用的是Advice
+methodInterceptor
组合实现一个切面Advisor
来实现AOP
的逻辑。实际上此实现也可以通过@Aspect
注解来实现, 在实际的工作过程中我感觉使用@Aspect
注解的方式更为简单, 更容易让人理解。
若在某个方法上同时使用@Transactional
和@Lock4j
那么锁对事物会不会有所影响?
这边先下结论: 1.上锁 -> 2.开启事务 -> 3.执行逻辑 -> 4.提交/回滚事务 -> 5.释放锁
1 | /** |
在创建锁切面对象时设置了Order
排序号为最小值, 所以若在某个方法上同时使用@Transactional
和@Lock4j
, 在代理类的advisors
列表中的第一个元素就是LockAnnotationAdvisor
, 第二个元素才是事物处理增强。