Spring底层事务原理

简介: Spring底层事务原理

Spring底层事务原理


Spring事务底层原理

一、@EnableTransactionManagement工作原理

二、Spring事务基本执行原理

三、Spring事务的过程

四、Spring事务传播机制

五、Spring事务传播机制分类

(1)案例分析、情况1

(2)案例分析、情况2

(3)案例分析、情况3

(4)案例分析、情况4

六、Spring事务强制回滚

七、TransactionSynchronization

一、@EnableTransactionManagement工作原理

开启Spring事务本质上就是增加了一个Advisor,但我们使用 @EnableTransactionManagement注解来开启Spring事务是,该注解代理的功能就是向Spring容器中添加了两个Bean:

(1)AutoProxyRegistrar
(2)ProxyTransactionManagementConfiguration

(1)AutoProxyRegistrar

主要的作用是向Spring容器中注册了一个InfrastructureAdvisorAutoProxyCreator的Bean。

而InfrastructureAdvisorAutoProxyCreator继承了AbstractAdvisorAutoProxyCreator,所以这个类的主要作用就是开启自动代理的作用,也就是一个BeanPostProcessor,会在初始化后步骤中去寻找Advisor类型的Bean,并判断当前某个Bean是否有匹配的Advisor,是否需要利用动态代理产生一个代理对象。

(2)ProxyTransactionManagementConfiguration是一个配置类,它又定义了另外三个bean:

image.png

二、Spring事务基本执行原理

一个Bean在执行Bean的创建生命周期时,会经过InfrastructureAdvisorAutoProxyCreator的初始化后的方法,会判断当前Bean对象是否BeanFactoryTransactionAttributeSourceAdvisor匹配,匹配逻辑为判断该Bean的类上是否存在@Transactional注解,或者类中的某个方法上是否存在@Transactional注解,如果存在则表示该Bean需要进行动态代理产生一个代理对象作为Bean对象。

该代理对象在执行某个方法时,会再次判断当前执行的方法是否和BeanFactoryTransactionAttributeSourceAdvisor匹配,如果匹配则执行该Advisor中的TransactionInterceptor的invoke()方法,

执行基本流程为:

利用所配置的PlatformTransactionManager事务管理器新建一个数据库连接

修改数据库连接的autocommit为false

执行MethodInvocation.proceed()方法,简单理解就是执行业务方法,其中就会执行sql

如果没有抛异常,则提交

如果抛了异常,则回滚

三、Spring事务的过程

1、数据库:建立连接、开启事务、进行sql操作、成功提交、失败回滚

2、业务逻辑:准备工作(可以进行前置通知)、开启事务、事务操作、成功提交(可以后置通知)、失败回滚(异常通知)

spring的事务是由aop实现的,首先要生成具体的代理对象,然后按照aop流程执行具体

的操作逻辑,正常情况下要通过通知来完成核心功能,但是事务部署通过通知来实现的,

而是通过TransactionInterceptor来实现的,然后调用invoke来实现具体的逻辑。

步骤如下:

1、先做准备工作,解析各个方法上事务相关的属性,根据具体的属性来判断是否开始新事务。

2、当需要开启的时候获取数据库连接,关闭自动提交功能,开启事务。

3、执行具体的sql逻辑操作,在操作的过程中如果执行失败会通过 completeTransactionafterthrowing来完成事务的回滚操作,回滚的

具体逻辑是通过dorollback方法实现,实现时也要先获取连接对象,

然后通过连接对象进行回滚(conn.rollback)。

4、如果执行成功,那么通过completeTransactionafterrunning来完成事务的提交操作,

具体逻辑是通过docommit方法来实现,实现的时候也是先获取连接,通过连接对象来

进行提交(conn.commit)。

5、最后事务执行完毕需要清除事务相关的事务信息(cleanupTransactioninfo)。

四、Spring事务传播机制

在开发过程中,经常会出现一个方法调用另外一个方法,那么这里就涉及到了多种场景,比如a()调用b():

a()和b()方法中的所有sql需要在同一个事务中吗?

a()和b()方法中的所有sql需要在同一个事务中吗?

a()需要在事务中执行,b()还需要在事务中执行吗?

或者其他情况

这种情况下就要求Spring事务能支持上面各种场景,这就是Spring事务传播机制的由来。

那Spring事务传播机制是如何实现的呢?

先描述其中一个场景中情况,a()在一个事务中执行,调用b()方法时需要新开一个事务执行:

代理对象执行a()方法前,先利用事务管理器新建一个数据库连接a

将数据库连接a的autocommit改为false

把数据库连接a设置到ThreadLocal中

执行a()方法中的sql

执行a()方法过程中,调用了b()方法(注意用代理对象调用b()方法)

a()方法正常执行完,则从ThreadLocal中拿到数据库连接a进行提交

关于步骤5的一些详细解释:

1、代理对象执行b()方法前,判断出来了当前线程中已经存在一个数据库连接a了,表示当前线程其实已经拥有一个Spring事务了,则进行挂起

2、挂起就是把ThreadLocal中的数据库连接a从ThreadLocal中移除,并放入一个挂起资源对象中

3、挂起完成后,再次利用事务管理器新建一个数据库连接b

4、将数据库连接b的autocommit改为false

5、把数据库连接b设置到ThreadLocal中

6、执行b()方法中的sql

7、b()方法正常执行完,则从ThreadLocal中拿到数据库连接b进行提交

8、提交之后会恢复所挂起的数据库连接a,这里的恢复,其实只是把在挂起资源对象中所保存的数据库连接a再次设置到ThreadLocal中

过程中最为重要的是:在执行某个方法时,判断当前是否已经存在一个事务,就是判断当前线程的ThreadLocal中是否存在一个数据库连接对象,如果存在则表示已经存在一个事务了。

五、Spring事务传播机制分类

在这里面,以非事务方式运行,表示以非Spring事务运行,表示在执行这个方法时,Spring事务管理器不会去建立数据库连接,执行sql时,由Mybatis或JdbcTemplate自己来建立数据库连接来执行sql。

(1)案例分析、情况1

默认情况下传播机制为REQUIRED,表示当前如果没有事务则新建一个事务,如果有事务则在当前事务中执行。

@Component
public class UserService {
  @Autowired
  private UserService userService;
  @Transactional
  public void test() {
      // test方法中的sql
      userService.a();
  }
  @Transactional
  public void a() {
      // a方法中的sql
  }
}

所以情况1的执行流程如下:

1、新建一个数据库连接conn
2、设置conn的autocommit为false
3、执行test方法中的sql
4、执行a方法中的sql
5、执行conn的commit()方法进行提交

(2)案例分析、情况2

如果是这种情况:

@Component
public class UserService {
  @Autowired
  private UserService userService;
  @Transactional
  public void test() {
      // test方法中的sql
      userService.a();
        int result = 100/0;
  }
  @Transactional
  public void a() {
      // a方法中的sql
  }
}

所以情况2的执行流程如下:

1、新建一个数据库连接conn
2、设置conn的autocommit为false
3、执行test方法中的sql
4、执行a方法中的sql
5、抛出异常
6、执行conn的rollback()方法进行回滚,所以两个方法中的sql都会回滚掉

(3)案例分析、情况3

@Component
public class UserService {
  @Autowired
  private UserService userService;
  @Transactional
  public void test() {
      // test方法中的sql
      userService.a();
  }
  @Transactional
  public void a() {
      // a方法中的sql
        int result = 100/0;
  }
}

所以情况3的执行流程如下:

1、新建一个数据库连接conn
2、设置conn的autocommit为false
3、执行test方法中的sql
4、执行a方法中的sql
5、抛出异常
6、执行conn的rollback()方法进行回滚,所以两个方法中的sql都会回滚掉

(4)案例分析、情况4

@Component
public class UserService {
  @Autowired
  private UserService userService;
  @Transactional
  public void test() {
      // test方法中的sql
    userService.a();
  }
  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public void a() {
      // a方法中的sql
      int result = 100/0;
  }
}

所以情况3的执行流程如下:

1、新建一个数据库连接conn
2、设置conn的autocommit为false
3、执行test方法中的sql
4、又新建一个数据库连接conn2
5、执行a方法中的sql
6、抛出异常
7、执行conn2的rollback()方法进行回滚
8、继续抛异常,对于test()方法而言,它会接收到一个异常,然后抛出
9、执行conn的rollback()方法进行回滚,最终还是两个方法中的sql都回滚了

六、Spring事务强制回滚

正常情况下,a()调用b()方法时,如果b()方法抛了异常,但是a()方法捕获了,那么a()的事务还是会正常提交的,但是有的时候,我们捕获异常可能只是不把异常信息返回给客户端,而是为了返回一些更优良的错误信息,所以在这个时候,我们还是希望事务能回滚的,那就得告诉Spring把当前事务回滚掉,做法就是:

@Transactional
public void test(){
    // 执行sql
  try {
      b();
  } catch (Exception e) {
    // 构造友好的错误信息返回
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
  }
}
public void b() throws Exception {
  throw new Exception();
}

七、TransactionSynchronization

Spring事务有可能会提交,回滚、挂起、恢复,所以Spring事务提供了一种机制,可以让程序员来监听当前Spring事务所处于的状态。

@Component
public class UserService {
 @Autowired
 private JdbcTemplate jdbcTemplate;
 @Autowired
 private UserService userService;
 @Transactional
 public void test(){
  TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
   @Override
   public void suspend() {
    System.out.println("test被挂起");
   }
   @Override
   public void resume() {
    System.out.println("test被恢复");
   }
   @Override
   public void beforeCommit(boolean readOnly) {
    System.out.println("test准备要提交");
   }
   @Override
   public void beforeCompletion() {
    System.out.println("test准备要提交或回滚");
   }
   @Override
   public void afterCommit() {
    System.out.println("test提交成功");
   }
   @Override
   public void afterCompletion(int status) {
    System.out.println("test提交或回滚成功");
   }
  });
  jdbcTemplate.execute("insert into t1 values(1,1,1,1,'1')");
  System.out.println("test");
  userService.a();
 }
 @Transactional(propagation = Propagation.REQUIRES_NEW)
 public void a(){
  TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
   @Override
   public void suspend() {
    System.out.println("a被挂起");
   }
   @Override
   public void resume() {
    System.out.println("a被恢复");
   }
   @Override
   public void beforeCommit(boolean readOnly) {
    System.out.println("a准备提交");
   }
   @Override
   public void beforeCompletion() {
    System.out.println("a准备提交或回滚");
   }
   @Override
   public void afterCommit() {
    System.out.println("a提交成功");
   }
   @Override
   public void afterCompletion(int status) {
    System.out.println("a提交或回滚成功");
   }
  });
  jdbcTemplate.execute("insert into t1 values(2,2,2,2,'2')");
  System.out.println("a");
 }
}
相关文章
|
3天前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
9 0
|
3月前
|
安全 Java 数据库
一天十道Java面试题----第四天(线程池复用的原理------>spring事务的实现方式原理以及隔离级别)
这篇文章是关于Java面试题的笔记,涵盖了线程池复用原理、Spring框架基础、AOP和IOC概念、Bean生命周期和作用域、单例Bean的线程安全性、Spring中使用的设计模式、以及Spring事务的实现方式和隔离级别等知识点。
|
3月前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
11天前
|
Java 开发者 Spring
Spring高手之路24——事务类型及传播行为实战指南
本篇文章深入探讨了Spring中的事务管理,特别是事务传播行为(如REQUIRES_NEW和NESTED)的应用与区别。通过详实的示例和优化的时序图,全面解析如何在实际项目中使用这些高级事务控制技巧,以提升开发者的Spring事务管理能力。
24 1
Spring高手之路24——事务类型及传播行为实战指南
|
4天前
|
XML Java 数据库连接
Spring中的事务是如何实现的
Spring中的事务管理机制通过一系列强大的功能和灵活的配置选项,为开发者提供了高效且可靠的事务处理手段。无论是通过注解还是AOP配置,Spring都能轻松实现复杂的事务管理需求。掌握这些工具和最佳实践,能
12 3
|
2月前
|
Java 数据库连接 数据库
spring复习05,spring整合mybatis,声明式事务
这篇文章详细介绍了如何在Spring框架中整合MyBatis以及如何配置声明式事务。主要内容包括:在Maven项目中添加依赖、创建实体类和Mapper接口、配置MyBatis核心配置文件和映射文件、配置数据源、创建sqlSessionFactory和sqlSessionTemplate、实现Mapper接口、配置声明式事务以及测试使用。此外,还解释了声明式事务的传播行为、隔离级别、只读提示和事务超时期间等概念。
spring复习05,spring整合mybatis,声明式事务
|
1月前
|
Java Spring 容器
Spring底层原理大致脉络
Spring底层原理大致脉络
|
2月前
|
Java 测试技术 数据库
Spring事务传播机制(最全示例)
在使用Spring框架进行开发时,`service`层的方法通常带有事务。本文详细探讨了Spring事务在多个方法间的传播机制,主要包括7种传播类型:`REQUIRED`、`SUPPORTS`、`MANDATORY`、`REQUIRES_NEW`、`NOT_SUPPORTED`、`NEVER` 和 `NESTED`。通过示例代码和数据库插入测试,逐一展示了每种类型的运作方式。例如,`REQUIRED`表示如果当前存在事务则加入该事务,否则创建新事务;`SUPPORTS`表示如果当前存在事务则加入,否则以非事务方式执行;`MANDATORY`表示必须在现有事务中运行,否则抛出异常;
140 4
Spring事务传播机制(最全示例)
|
1月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
126 9
|
1月前
|
Java 关系型数据库 MySQL
Spring事务失效,我总结了这7个主要原因
本文详细探讨了Spring事务在日常开发中常见的七个失效原因,包括数据库不支持事务、类不受Spring管理、事务方法非public、异常被捕获、`rollbackFor`属性配置错误、方法内部调用事务方法及事务传播属性使用不当。通过具体示例和源码分析,帮助开发者更好地理解和应用Spring事务机制,避免线上事故。适合所有使用Spring进行业务开发的工程师参考。
29 2