Spring事务专题(五)聊聊Spring事务到底是如何实现的(1)

简介: Spring事务专题(五)聊聊Spring事务到底是如何实现的(1)

前言


本专题大纲:

aHR0cHM6Ly9naXRlZS5jb20vd3hfY2MzNDdiZTY5Ni9ibG9nSW1hZ2UvcmF3L21hc3Rlci9pbWFnZS0yMDIwMDgwOTE4MjA1MjUyMC5wbmc.png

本文为本专题倒数第二篇文章。

在上篇文章中我们一起学习了Spring中的事务抽象机制以及动手模拟了一下Spring中的事务管理机制,那么本文我们就通过源码来分析一下Spring中的事务管理到底是如何实现的,本文将选用Spring5.2.x版本。


从@EnableTransactionManagement开始


Spring事务管理的入口就是@EnableTransactionManagement注解,所以我们直接从这个注解入手,其源码如下:

public @interface EnableTransactionManagement {
  // 是否使用cglib代理,默认是jdk代理
  boolean proxyTargetClass() default false;
    // 使用哪种代理模式,Spring AOP还是AspectJ
  AdviceMode mode() default AdviceMode.PROXY;
    // 为了完成事务管理,会向容器中添加通知
    // 这个order属性代表了通知的执行优先级
    // 默认是最低优先级
  int order() default Ordered.LOWEST_PRECEDENCE;
}

需要注意的是,@EnableTransactionManagement的proxyTargetClass会影响Spring中所有通过自动代理生成的对象。如果将proxyTargetClass设置为true,那么意味通过@EnableAspectJAutoProxy所生成的代理对象也会使用cglib进行代理。关于@EnableTransactionManagement跟@EnableAspectJAutoProxy混用时的一些问题等我们在对@EnableTransactionManagement有一定了解后再专门做一个比较,现在我们先来看看这个注解到底在做了什么?

aHR0cHM6Ly9naXRlZS5jb20vd3hfY2MzNDdiZTY5Ni9ibG9nSW1hZ2UvcmF3L21hc3Rlci8lRTQlQkElOEIlRTUlOEElQTElRTYlQjMlQTglRTglQTclQTMlRTUlODglODYlRTYlOUUlOTAucG5n.png

从上图中可以看出这个注解做的就是向容器中注册了AutoProxyRegistrar跟一个ProxyTransactionManagementConfiguration(这里就不考虑AspectJ了,我们平常都是使用SpringAOP),


AutoProxyRegistrar用于开启自动代理,其源码如下:


AutoProxyRegistrar分析


这个类实现了ImportBeanDefinitionRegistrar,它的作用是向容器中注册别的BeanDefinition,我们直接关注它的registerBeanDefinitions方法即可

//  AnnotationMetadata,代表的是AutoProxyRegistrar的导入类的元信息
// 既包含了类元信息,也包含了注解元信息
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    boolean candidateFound = false;
    // 获取@EnableTransactionManagement所在配置类上的注解元信息
    Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
    // 遍历注解
    for (String annType : annTypes) {
        // 可以理解为将注解中的属性转换成一个map
        AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
        if (candidate == null) {
            continue;
        }
        // 直接从map中获取对应的属性
        Object mode = candidate.get("mode");
        Object proxyTargetClass = candidate.get("proxyTargetClass");
        // mode,代理模型,一般都是SpringAOP
        // proxyTargetClass,是否使用cglib代理
        if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
            Boolean.class == proxyTargetClass.getClass()) {
            // 注解中存在这两个属性,并且属性类型符合要求,表示找到了合适的注解
            candidateFound = true;
            // 实际上会往容器中注册一个InfrastructureAdvisorAutoProxyCreator
            if (mode == AdviceMode.PROXY) {
                AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
                if ((Boolean) proxyTargetClass) {
                    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
                    return;
                }
            }
        }
    }
    // ......
}

@EnableTransactionManagement跟@EnableAspectJAutoProxy


如果对AOP比较了解的话,那么应该知道@EnableAspectJAutoProxy注解也向容器中注册了一个能实现自动代理的bd,那么当@EnableAspectJAutoProxy跟@EnableTransactionManagement同时使用会有什么问题吗?答案大家肯定知道,不会有问题,那么为什么呢?我们查看源码会发现,@EnableAspectJAutoProxy最终调用的是


AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary,其源码如下

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
    BeanDefinitionRegistry registry, @Nullable Object source) {
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

@EnableTransactionManagement最终调用的是,AopConfigUtils#registerAutoProxyCreatorIfNecessary,其源码如下

public static BeanDefinition registerAutoProxyCreatorIfNecessary(
    BeanDefinitionRegistry registry, @Nullable Object source) {
    return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}

它们最终都会调用registerOrEscalateApcAsRequired方法,只不过传入的参数不一样而已,一个是AnnotationAwareAspectJAutoProxyCreator,另一个是InfrastructureAdvisorAutoProxyCreator。


registerOrEscalateApcAsRequired源码如下:

private static BeanDefinition registerOrEscalateApcAsRequired(
    Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            // 当前已经注册到容器中的Bean的优先级
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            // 当前准备注册到容器中的Bean的优先级
            int requiredPriority = findPriorityForClass(cls);
            // 谁的优先级大就注册谁,AnnotationAwareAspectJAutoProxyCreator是最大的
            // 所以AnnotationAwareAspectJAutoProxyCreator会覆盖别的Bean
            if (currentPriority < requiredPriority) {
                apcDefinition.setBeanClassName(cls.getName());
            }
        }
        return null;
    }
  // 注册bd
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}

InfrastructureAdvisorAutoProxyCreator跟AnnotationAwareAspectJAutoProxyCreator的优先级是如何定义的呢?我们来看看AopConfigUtils这个类中的一个静态代码块

static {
    APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
    APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
    APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
}

实际上它们的优先级就是在APC_PRIORITY_LIST这个集合中的下标,下标越大优先级越高,所以AnnotationAwareAspectJAutoProxyCreator的优先级最高,所以AnnotationAwareAspectJAutoProxyCreator会覆盖InfrastructureAdvisorAutoProxyCreator,那么这种覆盖会不会造成问题呢?答案肯定是不会的,因为你用了这么久了也没出过问题嘛~那么再思考一个问题,为什么不会出现问题呢?这是因为InfrastructureAdvisorAutoProxyCreator只会使用容器内部定义的Advisor,但是AnnotationAwareAspectJAutoProxyCreator会使用所有实现了Advisor接口的通知,也就是说AnnotationAwareAspectJAutoProxyCreator的作用范围大于InfrastructureAdvisorAutoProxyCreator,因此这种覆盖是没有问题的。限于篇幅原因这个问题我不做详细解答了,有兴趣的同学可以看下两个类的源码。


@EnableTransactionManagement除了注册了一个AutoProxyRegistrar外,还向容器中注册了一个ProxyTransactionManagementConfiguration。


那么这个ProxyTransactionManagementConfiguration有什么作用呢?


如果大家对我文章的风格有一些了解的话就会知道,分析一个类一般有两个切入点

1.它的继承关系

2.它提供的API

大家自己在阅读源码时也可以参考这种思路,分析一个类的继承关系可以让我们了解它从抽象到实现的过程,即使不去细看API也能知道它的大体作用。仅仅知道它的大致作用是不够的,为了更好了解它的细节我们就需要进一步去探索它的具体实现,也就是它提供的API。这算是我看了这么就源码的一点心得,正好想到了所以在这里分享下,之后会专门写一篇源码心得的文章


ProxyTransactionManagementConfiguration分析


继承关系


aHR0cHM6Ly9naXRlZS5jb20vd3hfY2MzNDdiZTY5Ni9ibG9nSW1hZ2UvcmF3L21hc3Rlci9Qcm94eVRyYW5zYWN0aW9uTWFuYWdlbWVudENvbmZpZ3VyYXRpb24ucG5n.png

这个类的继承关系还是很简单的,只有一个父类

AbstractTransactionManagementConfiguration


AbstractTransactionManagementConfiguration


源码如下:

@Configuration
public abstract class AbstractTransactionManagementConfiguration implements ImportAware {
  @Nullable
  protected AnnotationAttributes enableTx;
  @Nullable
  protected TransactionManager txManager;
  // 这个方法就是获取@EnableTransactionManagement的属性
    // importMetadata:就是@EnableTransactionManagement这个注解所在类的元信息
  @Override
  public void setImportMetadata(AnnotationMetadata importMetadata) {
        // 将EnableTransactionManagement注解中的属性对存入到map中
        // AnnotationAttributes实际上就是个map
    this.enableTx = AnnotationAttributes.fromMap(       importMetadata.getAnnotationAttributes(EnableTransactionManagement.class.getName(), false));
        // 这里可以看到,限定了导入的注解必须使用@EnableTransactionManagement
        if (this.enableTx == null) {
            throw new IllegalArgumentException(
                "@EnableTransactionManagement is not present on importing class " + importMetadata.getClassName());
        }
  }
    // 我们可以配置TransactionManagementConfigurer
    // 通过TransactionManagementConfigurer向容器中注册一个事务管理器
    // 一般不会这么使用,更多的是通过@Bean的方式直接注册
  @Autowired(required = false)
  void setConfigurers(Collection<TransactionManagementConfigurer> configurers) {
    // .....
    TransactionManagementConfigurer configurer = configurers.iterator().next();
    this.txManager = configurer.annotationDrivenTransactionManager();
  }
    // 向容器中注册一个TransactionalEventListenerFactory
    // 这个类用于处理@TransactionalEventListener注解
    // 可以实现对事件的监听,并且在事务的特定阶段对事件进行处理
  @Bean(name = TransactionManagementConfigUtils.TRANSACTIONAL_EVENT_LISTENER_FACTORY_BEAN_NAME)
  @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  public static TransactionalEventListenerFactory transactionalEventListenerFactory() {
    return new TransactionalEventListenerFactory();
  }
}

TransactionalEventListenerFactory


上面的代码中大家可能比较不熟悉的就是TransactionalEventListenerFactory,这个类主要是用来处理@TransactionalEventListener注解的,我们来看一个实际使用的例子

@Component
public class DmzListener {
    // 添加一个监听器
    // phase = TransactionPhase.AFTER_COMMIT意味着这个方法在事务提交后执行
  @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
  public void listen(DmzTransactionEvent transactionEvent){
    System.out.println("事务已提交");
  }
}
// 定义一个事件
public class DmzTransactionEvent extends ApplicationEvent {
  public DmzTransactionEvent(Object source) {
    super(source);
  }
}
@Component
public class DmzService {
  @Autowired
  ApplicationContext applicationContext;
    // 一个需要进行事务管理的方法
  @Transactional
  public void invokeWithTransaction() {
        // 发布一事件
    applicationContext.publishEvent(new DmzTransactionEvent(this));
        // 以一条sout语句提代sql执行过程
    System.out.println("sql invoked");
  }
}
// 测试方法
public class Main {
  public static void main(String[] args) {
    AnnotationConfigApplicationContext ac =
        new AnnotationConfigApplicationContext(Config.class);
    DmzService dmzService = ac.getBean(DmzService.class);
    dmzService.invokeWithTransaction();
  }
}
// 最后程序会按顺序输出
// sql invoked
// 事务已提交

通过上面的例子我们可以看到,虽然我们在invokeWithTransaction方法中一开始就发布了一个事件,但是监听事件的方法却是在invokeWithTransaction才执行的,正常事件的监听是同步的,假设我们将上述例子中的@TransactionalEventListener注解替换成为@EventListener注解,如下:

@Component
public class DmzListener {
    // 添加一个监听器
  @EventListener
  public void listen(DmzTransactionEvent transactionEvent){
    System.out.println("事务已提交");
  }
}

这个时候程序的输出就会是

// 事务已提交
// sql invoked

那么@TransactionalEventListener注解是实现这种看似异步(实际上并不是)的监听方式的呢?

aHR0cHM6Ly9naXRlZS5jb20vd3hfY2MzNDdiZTY5Ni9ibG9nSW1hZ2UvcmF3L21hc3Rlci9UcmFuc2FjdGlvbmFsRXZlbnRMaXN0ZW5lckZhY3RvcnklRTglQjAlODMlRTclOTQlQTglRTklOTMlQkUucG5n.png

大家按照上面这个调用链可以找到这么一段代码

aHR0cHM6Ly9naXRlZS5jb20vd3hfY2MzNDdiZTY5Ni9ibG9nSW1hZ2UvcmF3L21hc3Rlci9pbWFnZS0yMDIwMDgxMDE4MDYxNjI4NC5wbmc (1).png

通过上面的代码,我们可以发现最终会调用到TransactionalEventListenerFactory的createApplicationListener方法,通过这个方法创建一个事件监听器然后添加到容器中,createApplicationListener方法源码如下:

public ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method) {
    return new ApplicationListenerMethodTransactionalAdapter(beanName, type, method);
}

就是创建了一个ApplicationListenerMethodTransactionalAdapter,这个类本身就是一个事件监听器(实现了ApplicationListener接口)。我们直接关注它的事件监听方法,也就是onApplicationEvent方法,其源码如下:

@Override
public void onApplicationEvent(ApplicationEvent event) {
    // 激活了同步,并且真实存在事务
    if (TransactionSynchronizationManager.isSynchronizationActive() &&
        TransactionSynchronizationManager.isActualTransactionActive()) {
        // 实际上是依赖事务的同步机制实现的事件监听
        TransactionSynchronization transactionSynchronization = createTransactionSynchronization(event);
        TransactionSynchronizationManager.registerSynchronization(transactionSynchronization);
    }
    // 在没有开启事务的情况下是否处理事件
    else if (this.annotation.fallbackExecution()) {
        // ....
        // 如果注解中的fallbackExecution为true,意味着没有事务开启的话
        // 也会执行监听逻辑
        processEvent(event);
    }
    else {
       // ....
    }
}

到这一步逻辑已经清楚了,@TransactionalEventListener所标注的方法在容器启动时被解析成了一个ApplicationListenerMethodTransactionalAdapter,这个类本身就是一个事件监听器,当容器中的组件发布了一个事件后,如果事件匹配,会进入它的onApplicationEvent方法,这个方法并没有直接执行我们所定义的监听逻辑,而是给当前事务注册了一个同步的行为,当事务到达某一个阶段时,这个行为会被触发。通过这种方式,实现一种伪异步。实际上注册到事务的的同步就是TransactionSynchronizationEventAdapter,这个类的源码非常简单,这里就单独取它一个方法看下

// 这个方法会在事务提交前执行
public void beforeCommit(boolean readOnly) {
    // 在执行时会先判断在@TransactionalEventListener注解中定义的phase是不是BEFORE_COMMIT
    // 如果不是的话,什么事情都不做
   if (this.phase == TransactionPhase.BEFORE_COMMIT) {
      processEvent();
   }
}

别看上面这么多内容,到目前为止我们还是只对ProxyTransactionManagementConfiguration的父类做了介绍,接下来我们就来看看ProxyTransactionManagementConfiguration自身做了什么事情。

相关文章
|
19天前
|
缓存 安全 Java
Spring高手之路26——全方位掌握事务监听器
本文深入探讨了Spring事务监听器的设计与实现,包括通过TransactionSynchronization接口和@TransactionalEventListener注解实现事务监听器的方法,并通过实例详细展示了如何在事务生命周期的不同阶段执行自定义逻辑,提供了实际应用场景中的最佳实践。
40 2
Spring高手之路26——全方位掌握事务监听器
|
20天前
|
Java 关系型数据库 数据库
京东面试:聊聊Spring事务?Spring事务的10种失效场景?加入型传播和嵌套型传播有什么区别?
45岁老架构师尼恩分享了Spring事务的核心知识点,包括事务的两种管理方式(编程式和声明式)、@Transactional注解的五大属性(transactionManager、propagation、isolation、timeout、readOnly、rollbackFor)、事务的七种传播行为、事务隔离级别及其与数据库隔离级别的关系,以及Spring事务的10种失效场景。尼恩还强调了面试中如何给出高质量答案,推荐阅读《尼恩Java面试宝典PDF》以提升面试表现。更多技术资料可在公众号【技术自由圈】获取。
|
1月前
|
Java 开发者 Spring
Spring高手之路24——事务类型及传播行为实战指南
本篇文章深入探讨了Spring中的事务管理,特别是事务传播行为(如REQUIRES_NEW和NESTED)的应用与区别。通过详实的示例和优化的时序图,全面解析如何在实际项目中使用这些高级事务控制技巧,以提升开发者的Spring事务管理能力。
52 1
Spring高手之路24——事务类型及传播行为实战指南
|
25天前
|
JavaScript Java 关系型数据库
Spring事务失效的8种场景
本文总结了使用 @Transactional 注解时事务可能失效的几种情况,包括数据库引擎不支持事务、类未被 Spring 管理、方法非 public、自身调用、未配置事务管理器、设置为不支持事务、异常未抛出及异常类型不匹配等。针对这些情况,文章提供了相应的解决建议,帮助开发者排查和解决事务不生效的问题。
|
1月前
|
XML Java 数据库连接
Spring中的事务是如何实现的
Spring中的事务管理机制通过一系列强大的功能和灵活的配置选项,为开发者提供了高效且可靠的事务处理手段。无论是通过注解还是AOP配置,Spring都能轻松实现复杂的事务管理需求。掌握这些工具和最佳实践,能
47 3
|
3月前
|
Java 数据库连接 数据库
spring复习05,spring整合mybatis,声明式事务
这篇文章详细介绍了如何在Spring框架中整合MyBatis以及如何配置声明式事务。主要内容包括:在Maven项目中添加依赖、创建实体类和Mapper接口、配置MyBatis核心配置文件和映射文件、配置数据源、创建sqlSessionFactory和sqlSessionTemplate、实现Mapper接口、配置声明式事务以及测试使用。此外,还解释了声明式事务的传播行为、隔离级别、只读提示和事务超时期间等概念。
spring复习05,spring整合mybatis,声明式事务
|
2月前
|
Java 关系型数据库 MySQL
Spring事务失效,我总结了这7个主要原因
本文详细探讨了Spring事务在日常开发中常见的七个失效原因,包括数据库不支持事务、类不受Spring管理、事务方法非public、异常被捕获、`rollbackFor`属性配置错误、方法内部调用事务方法及事务传播属性使用不当。通过具体示例和源码分析,帮助开发者更好地理解和应用Spring事务机制,避免线上事故。适合所有使用Spring进行业务开发的工程师参考。
39 2
|
2月前
|
Java 程序员 Spring
Spring事务的1道面试题
每次聊起Spring事务,好像很熟悉,又好像很陌生。本篇通过一道面试题和一些实践,来拆解几个Spring事务的常见坑点。
Spring事务的1道面试题
|
3月前
|
Java Spring
Spring 事务传播机制是什么?
Spring 事务传播机制是什么?
27 4
|
2月前
|
监控 Java 数据库
Spring事务中的@Transactional注解剖析
通过上述分析,可以看到 `@Transactional`注解在Spring框架中扮演着关键角色,它简化了事务管理的复杂度,让开发者能够更加专注于业务逻辑本身。合理运用并理解其背后的机制,对于构建稳定、高效的Java企业应用至关重要。
71 0
下一篇
DataWorks