Spring事务的实现原理(@Transactional原理 + 失效场景)

如果你正打算深入学习Spring,但是不知从何学起,那么我强烈推荐你可以按照这个系列做一遍。本系列将Spring框架的各个部分从它庞杂的代码体系中抽取出来,然后对每一个部分进行讲解,并最终搭建成简易版Spring。我以人格保证:如果你可以坚持做下来,那么你对Spring这块的知识就基本都掌握清楚了! 附上该系列地址:https://blog.csdn.net/zhang_qing_yun/article/details/120084497

Spring事务管理

Spring事务的配置方式

Spring支持两种事务配置方式:

  1. 编程式事务配置:使用TransactionTemplate(推荐)或者直接使用PlatformTransactionManager通过硬编码的方式在业务代码中来管理事务。
  2. 声明式事务配置:通过XML配置或者注解的方式来声明开启事务,然后让Spring来接管事务的实现逻辑,通过这种方式,我们只需要声明事务而不用去管理事务。声明式事务管理建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。

对比:编程式事务是侵入式的,对业务代码的编写有影响,并且每次使用都需要单独实现,非常繁琐;声明式事务属于无侵入式,不会影响业务逻辑的实现,只需要通过配置文件或者注解的方式声明事务便可以将事务规则应用到业务逻辑中。但是,声明式事务管理的粒度是方法级别,而编程式事务管理是可以到代码块的。

Spring的事务传播

首先,事务传播都是在一个线程内完成的;其次,所谓的事务传播行为是相对于内层被调用函数来说的,Spring提供了7种事务传播机制:

事务传播机制

@Transactional的实现原理

首先我们需要在主配置类上添加@EnableTransactionManagement注解来开启事务,这个注解通过@Import注解向容器中引入了两个类:AutoProxyRegistrar和ProxyTransactionManagementConfiguration。

AutoProxyRegistrar向容器中导入了InfrastructureAdvisorAutoProxyCreator,该类继承于AbstractAutoProxyCreator,作用类似于AOP,只不过是用于为加了@Transactional注解的类生成代理对象。

ProxyTransactionManagementConfiguration向容器中导入了3个Bean:AnnotationTransactionAttributeSource、TransactionInterceptor、BeanFactoryTransactionAttributeSourceAdvisor,其中前两个都是第三个的属性,而第三个是一个Advisor,前两者一个相当于切点,用于判断是否加了@Transactional注解,另一个是一个拦截器,具体事务管理的逻辑就是在该拦截器的invoke()方法中实现的。

整体的执行流程:当创建一个Bean时,会去执行AbstractAutoProxyCreator的postProcessAfterInitialization,在这个方法中会去判断是否需要为该Bean生成代理对象。这时会去获取所有的Advisor,然后遍历并从中找出与该Bean相匹配的,此时就会用BeanFactoryTransactionAttributeSourceAdvisor的属性AnnotationTransactionAttributeSource去对该Bean进行判断,如果该Bean或它的方法加了@Transactional注解则匹配成功(注意:非public方法加了该注解也没用,不匹配),将该BeanFactoryTransactionAttributeSourceAdvisor返回。由于返回值不为空,所以就会为该Bean创建代理对象。

当调用该代理对象的方法时,会先去获取该方法的拦截器链(遍历该Bean的所有Advisor,然后找到和该方法相匹配的Advisor)。BeanFactoryTransactionAttributeSourceAdvisor的属性AnnotationTransactionAttributeSource会去判断该方法是否有@Transactional注解,如果有则匹配成功,则将该Advisor的属性TransactionInterceptor添加到拦截器链中。然后从头开始遍历拦截器链(通过递归调用proceed()方法完成遍历),当执行TransactionInterceptor的invoke()方法时,会先去开启一个事务,然后再去递归调用proceed()方法向下遍历,直至执行完业务方法,如果在这个过程中出现了异常就回滚事务,否则就提交事务。

在整个事务过程中,如何保证操作数据库时使用的是同一个连接?在开启事务时,首先会从数据库连接池中获得一个connection,然后将这个连接与一个ThradLocal对象绑定起来,以后需要操作数据库时都通过该ThradLocal对象来获取connection,最后在事务提交或回滚后释放绑定关系,并将connection归还到数据库连接池中。这样,通过ThradLocal对象,我们就保证了操作的是同一个connection。(ThradLocal相关知识移步:https://blog.csdn.net/zhang_qing_yun/article/details/118884946

在这里插入图片描述

@Transactional失效的场景

  1. 非public的方法。原因就是在为该Bean寻找匹配的Advisor时,如果是非public方法加了该注解会忽视掉。
  2. 使用final修饰的方法在使用CGLIB动态代理时。原因就是CGLIB并不会代理final方法。
  3. 类内部的非事务方法调用事务方法。原因就是,事务方法的调用是通过this来完成的,跳过了代理对象。
  4. 自己捕获了异常。原因就是没有抛出新的异常,导致事务操作不会进行回滚。

具体可以参考:https://blog.csdn.net/qq_20597727/article/details/84900994

@TransactionalSpring框架中用来管理事务的注解,它可以将一组操作作为一个事务来执行,如果其中任何一个操作失败,则整个事务将被回滚,以保证数据的一致性和完整性。 @Transactional的工作原理是在方法执行前创建一个事务,方法执行完毕后,如果没有发生异常,则提交事务,否则回滚事务。这个过程是由Spring框架通过AOP技术实现的。 然而,在一些场景下,@Transactional可能会失效,比如: 1. 事务注解被应用在了非public方法上。只有public方法才能被Spring框架所代理,因此,如果事务注解被应用在了非public方法上,那么它将不会生效。 2. 事务注解被应用在了static方法上。同样地,Spring框架只会代理非static方法,因此,如果事务注解被应用在了static方法上,那么它将不会生效。 3. 异常被catch住了。如果在事务中发生了异常,但是异常被catch住了,那么事务将不会回滚。 4. 方法中调用了同一个类中的其他方法。如果在同一个类中的方法中调用了其他方法,那么事务注解可能会失效。这是因为Spring通过代理来实现事务管理,而代理只能拦截方法调用,无法拦截同类中的方法调用。 5. 多个事务注解嵌套使用。如果在一个方法中使用了多个@Transactional注解,并且它们的传播级别不同,那么可能会导致事务失效。这是因为Spring不支持嵌套事务的回滚。 了解这些场景可以帮助我们更好地使用@Transactional注解,从而保证数据的一致性和完整性。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值