0%

Lock4j的锁代理浅析

Lock4j的锁代理浅析

一、背景

​ 工作项目中使用baomidoulock4j来进行分布式锁相关处理。在使用中还是出现了一些状况, 不得不稍微理解下lock4j中的一些基本设计思路和方式。

二、基本知识

​ 在Spring中使用AOP实际上有好几种方式: 通过@AspectJ注解方式、通过xml配置文件方式、通过实现开放的一些抽象接口或者具体的实现方式。不论是使用哪种方式, 都需要指定出AOP中一些必备的结构。
这里简单描述下一些必需创建的结构:

  • 增强(Advice): 很多地方称为通知, 但实际上理解为增强更为准确. 用于表示具体的增强逻辑, 这里以日志前置增强为例, 前置增强顾名思义就是在某个逻辑之前添加保存日志的增强逻辑, 这里的日志保存逻辑即前置增强需要处理的逻辑, 所以前置增强的逻辑实际上就是一段处理逻辑代码, 和具体的目标逻辑之间没有关系.
  • 切入点(Pointcut): 用于表示具体要切入的地方, 以上述前置增强为例, 在编写好日志增强的逻辑代码之后, 要应用到多少个地方, 也就是多少个地方需要添加这个日志前置增强, 这里就需要由切入点来标记具体需要切入的地方。
  • 切面(Aspect): 切面可以简单理解为增强Advice和切入点Pointcut的组合.
  • 目标对象(Target Object): 用于表示具体被增强的对象, 因为在Spring中是通过动态代理来实现切面处理, 故被增强对象就是目标对象.
  • 代理对象(AOP Proxy): 用于表示将切面应用到目标对象而创建的代理对象。

三、浅析

​ 在lock4j项目中, 大致的锁代理机制为创建一个基于LockInterceptor环绕增强的Advice@Lock4j注解标记的切点PointcutLockAnnotationAdvisor切面。

  1. 环绕增强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
    38
    public 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:
    **/
    @Override
    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);
    }
    }
    }
    }
  2. 切点Pointcut:

    1
    2
    3
    4
    /**
    * 标记在方法上标记Lock4j注解的方法即为此aop的切点
    */
    private final Pointcut pointcut = AnnotationMatchingPointcut.forMethodAnnotation(Lock4j.class);
  3. 切面LockAnnotationAdvisor

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
38
39
public class LockAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {

private final Advice advice;

/**
* 标记在方法上标记Lock4j注解的方法即为此aop的切点
*/
private final Pointcut pointcut = AnnotationMatchingPointcut.forMethodAnnotation(Lock4j.class);

public LockAnnotationAdvisor(@NonNull LockInterceptor lockInterceptor, int order) {
this.advice = lockInterceptor;
setOrder(order);
}

@Override
public Pointcut getPointcut() {
return this.pointcut;
}

@Override
public Advice getAdvice() {
return this.advice;
}

/**
* 在增强中加入了bean工厂
*
* @Author: xiaocainiaoya
* @Date: 2021/12/29
* @param beanFactory
* @return: void
**/
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (this.advice instanceof BeanFactoryAware) {
((BeanFactoryAware) this.advice).setBeanFactory(beanFactory);
}
}
}

以上就实现了若在方法上标记了@Lock4j注解就会进入到LockInterceptor的增强逻辑上进行具体的加锁逻辑。
所以此实现采用的是Advice+methodInterceptor组合实现一个切面Advisor来实现AOP的逻辑。实际上此实现也可以通过@Aspect注解来实现, 在实际的工作过程中我感觉使用@Aspect注解的方式更为简单, 更容易让人理解。

若在某个方法上同时使用@Transactional@Lock4j那么锁对事物会不会有所影响?
这边先下结论: 1.上锁 -> 2.开启事务 -> 3.执行逻辑 -> 4.提交/回滚事务 -> 5.释放锁

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 这里标记这个切面的order = HIGHEST_PRECEDENCE = Integer.MIN_VALUE;(最小值)
*
* @Author: xiaocainiaoya
* @Date: 2021/12/29 11:44:15
* @param lockInterceptor
* @return:
**/
@Bean
@ConditionalOnMissingBean
public LockAnnotationAdvisor lockAnnotationAdvisor(LockInterceptor lockInterceptor) {
return new LockAnnotationAdvisor(lockInterceptor, Ordered.HIGHEST_PRECEDENCE);
}

在创建锁切面对象时设置了Order排序号为最小值, 所以若在某个方法上同时使用@Transactional@Lock4j, 在代理类的advisors列表中的第一个元素就是LockAnnotationAdvisor, 第二个元素才是事物处理增强。
锁代理.png

-------------本文结束感谢您的阅读-------------