Springboot之@Transactional事务注解原理详解


@Transactional注解的逻辑是通过动态代理来实现的,而生成这个动态代理类分成了两步:
1、向spring容器注册事务相关的切面逻辑
2、根据切面逻辑生成动态代理
下面围绕这两点来看下Springboot里的实现原理

注册事务切面逻辑

切面逻辑里有三个概念:
Pointcut:负责告诉spring容器哪个类需要增强
Advise:具体的切面逻辑,这里就是根据异常进行commit或者回滚的相关逻辑
Advisor:封装了AdvisePointcut的类
事务相关的这三个对象是由Springboot自动装载的(自动装载组件原理可以看这里),流程如下:

1、扫描spring-boot-autoconfigure依赖包下的META-INF/spring.factories,加载文件里的TransactionAutoConfiguration

在这里插入图片描述

2、解析TransactionAutoConfiguration类里的@EnableTransactionManagement注解,根据该注解上的@Import加载TransactionManagementConfigurationSelector

在这里插入图片描述

3、通过TransactionManagementConfigurationSelector类的selectImports方法加载ProxyTransactionManagementConfiguration

在这里插入图片描述

4、
ProxyTransactionManagementConfiguration类通过@Bean加载BeanFactoryTransactionAttributeSourceAdvisor类,该类实现了Pointcut,并且在该类里注入了实现了Advise接口的TransactionInterceptor

在这里插入图片描述

BeanFactoryTransactionAttributeSourceAdvisor类图如下:
在这里插入图片描述

到这里,Spring容器加载了需要实现事务相关切面的关键的三个对象,其中Pointcut的匹配逻辑就是看这个方法有没有被@Transactional注解标注,最终会调用到SpringTransactionAnnotationParser类的parseTransactionAnnotation方法里,有兴趣的同学可以在这里打上断点看下调用链,debug图如下:
在这里插入图片描述

这里主要看下事务的核心逻辑,这个核心逻辑就在实现了Advise接口的TransactionInterceptor类的invoke方法里,这里看下这里的源码:

public Object invoke(MethodInvocation invocation) throws Throwable {
    Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;
    Method var10001 = invocation.getMethod();
    invocation.getClass();
    // 调用事务逻辑
    return this.invokeWithinTransaction(var10001, targetClass, invocation::proceed);
}

@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, TransactionAspectSupport.InvocationCallback invocation) throws Throwable {
  TransactionAttributeSource tas = this.getTransactionAttributeSource();
  // 获取改方法上的事务配置,包括传播级别、异常信息等配置
  TransactionAttribute txAttr = tas != null ? tas.getTransactionAttribute(method, targetClass) : null;
  // 事务管理器,负责生成事务上下文信息,比如开启事务、获取数据库链接等逻辑
  TransactionManager tm = this.determineTransactionManager(txAttr);
  ...
  PlatformTransactionManager ptm = this.asPlatformTransactionManager(tm);
  String joinpointIdentification = this.methodIdentification(method, targetClass, txAttr);
  // 根据传播级别配置,看是否需要新建事务
  TransactionAspectSupport.TransactionInfo txInfo = this.createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);

  Object retVal;
  // 通过try catch捕获异常来实现回滚逻辑
  try {
  // 调用真正的dao层逻辑
      retVal = invocation.proceedWithInvocation();
  } catch (Throwable var18) {
  // 根据@Transactional配置的异常来决定是否回滚
      this.completeTransactionAfterThrowing(txInfo, var18);
      throw var18;
  } finally {
  // 结束当前的事务,信息是保存在ThreadLocal里
      this.cleanupTransactionInfo(txInfo);
  }

  if (retVal != null && vavrPresent && TransactionAspectSupport.VavrDelegate.isVavrTry(retVal)) {
      TransactionStatus status = txInfo.getTransactionStatus();
      if (status != null && txAttr != null) {
          retVal = TransactionAspectSupport.VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
      }
  }
  // 没有异常时,执行commit操作
  this.commitTransactionAfterReturning(txInfo);
  return retVal;
  ...
  
}

总结下核心步骤如下:

通过动态代理为标注了@Transactional注解的方法增加切面逻辑,而事务的上下文包括数据库链接都是通过ThreadLocal来传递,在这个切面逻辑里主要做这几个事情:

  1. 获取方法上标注的注解的元数据,包括传播级别、异常配置等信息
  2. 通过ThreadLocal获取事务上下文,检查是否已经激活事务
  3. 如果已经激活事务,则根据传播级别配置,看是否需要新建事务(如果新建事务,会生成一个新的事务上下文对象TransactionInfo,并将上一个事务上下文赋值到新上下文的oldTransactionInfo属性上)代码位置在TransactionAspectSupport类的prepareTransactionInfo方法里的bindToThread方法里
  4. 开启事务,先通过数据库连接池获取链接,关闭链接的autocommit,然后在try catch里反射执行真正的dao操作,通过异常情况来决定是commit还是rollback

下面看下Springboot怎么根据这三个对象来生成代理类的。

根据切面逻辑生成代理类

核心逻辑是Springboot会自动装载AopAutoConfiguration类(自动装载组件原理可以看这里),该类会根据加载到Spring容器里的Advisor对象来找到需要增强的类,并生成代理类,核心步骤如下:

1、加载AopAutoConfiguration类,根据proxyTargetClass配置来选择是使用jdk的proxy还是cglib来生成动态代理类,截图如下:

在这里插入图片描述

2、解析@EnableAspectJAutoProxy注解,通过该注解上的@Import加载AspectJAutoProxyRegistrar

在这里插入图片描述

3、AspectJAutoProxyRegistrar类会向Spring容器注册AnnotationAwareAspectJAutoProxyCreator类,该类实现了BeanPostProcessor接口,所以这个aop逻辑是在Springbean生成过程中通过后置处理器逻辑来实现的。

在这里插入图片描述
AnnotationAwareAspectJAutoProxyCreator类图如下:
在这里插入图片描述

4、生成动态代理类的核心逻辑在AspectJAutoProxyRegistrar类的父类AbstractAutoProxyCreator类的postProcessAfterInitialization方法里,该方法实现的是后置处理器BeanPostProcessor类,用来生成动态代理类,这里看下具体逻辑:

public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            // 生成动态代理类
            return this.wrapIfNecessary(bean, beanName, cacheKey);
        }
    }

    return bean;
}


protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    ...
    // 遍历注册到spring容器内的advisor,根据class信息找到匹配到的advisor
    Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, (TargetSource)null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 生成动态代理类
        Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    } else {
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
    ...
}

通过bean找到匹配的advisor的debug图如下,可以看到这里的BeanFactoryTransactionAttributeSourceAdvisor就是上面注入的。
在这里插入图片描述

总结

可以看出Springboot里的@Transactional之所以能起作用,是由两个自动装载类配合的,一个就是负责生成动态代理类的AopAutoConfiguration类,一个就是TransactionAutoConfiguration类,负责向Spring容器注册事务相关的Advisor。这里也给我们提供了扩展,如果我们需要自定义自己的切面逻辑,只需要向spring容器注册自定义的Advisor,定义好PointcutAdvise就行了。

  • 6
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值