Java教程

Spring中事务源码解读

本文主要是介绍Spring中事务源码解读,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

前言

之前的文章是解析spring中ioc源码 以及 aop源码 ,包括核心的bean的生命周期 以及 各个扩展部分,以及 aop源码 如何开启注解时, 解析注解标签时,将 所有 aop所拥有的控件在bean实例化 之前 和实例化之后的一个 扩展 AnnotationAwareAspectJAutoProxyCreator 这个类上 面做的所有的处理和扩展。本篇文章会继续 研究 事务源码部分, 包括事务隔离级别,以及 事务如何实现的。

Spring事务管理

​​​​​​Data Access (spring.io)

Spring 框架为事务管理提供一个一套统一的抽象,带来的好处有: 1. 跨不同事务 API 的统一的编程模型,无论你使用的是 jdbc 、 jta 、 jpa 、 hibernate 。 2. 支持声明式事务 3. 简单的事务管理 API 4. 能与 spring 的数据访问抽象层完美集成

 Spring框架的事务支持模型的优势

传统上,Java EE 开发人员对事务管理有两种选择:全局事务或本地事务,这两者都有很大的局限性。接下来的两节将回顾全局和本地事务管理,然后讨论Spring框架的事务管理支持如何解决全局和本地事务模型的局限性。

事务概念

Isolation 隔离级别 此事务与其他事务的工作隔离的程度。例如,该事务能否看到来自其他事务的未提交的写操作
  • READ_UNCOMMITTED 读未提交
  • READ_COMMITTED 读已提交
  • REPEATABLE_READ 可重复读
  • SERIALIZABLE 序列化(串行)
Read/Write 读写 该事务操作是读、是写、还是有读有写 Timeout 超时 对事务执行的时长设置一个阀值,如果超过阀值还未完成则回滚。 Propagation 传播行为 当一个方法开启事务后,在方法中调用了其他的方法,其他方法可能也需要事务管理,此时就涉及事务该如何传播了。 1. TransactionDefinition.PROPAGATION_REQUIRED :如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。 2. TransactionDefinition.PROPAGATION_REQUIRES_NEW :创建一个新的事务,如果当前存在事务,则把当前事务挂起。 3. TransactionDefinition.PROPAGATION_SUPPORTS :如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。 4. TransactionDefinition.PROPAGATION_NOT_SUPPORTED :以非事务方式运行,如果当前存在事务,则把当前事务挂起。 5. TransactionDefinition.PROPAGATION_NEVER :以非事务方式运行,如果当前存在事务,则抛出异常。 6. TransactionDefinition.PROPAGATION_MANDATORY :如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。 7. TransactionDefinition.PROPAGATION_NESTED :如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 TransactionDefinition.PROPAGATION_REQUIRED。 SavePoint 保存点 事务中可以设置一些保存点(阶段标识),回滚时可以指定回滚到前面的哪个保存点。 Commit/Rollback 提交 / 回滚 提交、回滚事务

Spring中的事务使用

<!--transication--> 
	<dependency> 
		<groupId>org.springframework</groupId> 
		<artifactId>spring-jdbc</artifactId> 
		<version>5.2.8.RELEASE</version>
	</dependency> 
	<dependency> 
		<groupId>com.alibaba</groupId> 
		<artifactId>druid</artifactId> 
		<version>1.2.1</version> 
	</dependency> 
	<dependency> 
		<groupId>mysql</groupId> 
		<artifactId>mysql-connector-java</artifactId> 
		<version>8.0.19</version> 
	</dependency> 
	<!-- jta api --> 
	<dependency> 
		<groupId>javax.transaction</groupId> 
		<artifactId>javax.transaction-api</artifactId> 
		<version>1.3</version> 
	</dependency> 
	<!-- atomikos 数据库的TM组件 --> 
	<dependency> 
		<groupId>com.atomikos</groupId> 
		<artifactId>transactions-jdbc</artifactId> 
		<version>4.0.6</version> 
	</dependency> 
	<!-- atomikos JMS 有MQ需要事务管理时加入 --> 
	<dependency> 
		<groupId>com.atomikos</groupId> 
		<artifactId>transactions-jms</artifactId> 
		<version>4.0.6</version> 
		
	</dependency>

XML配置方式

application.xml 文件中配置
<!-- 配置事务管理器 --> 
<bean id="txManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
    <property name="dataSource" ref="dataSource"/>
 </bean>
Spring 事务管理起到的作用
  • 不配置事务运行Main类,查看数据库操作结果
  • 配置事务运行Main类,查看数据库操作结果
UserService 操作了 User 、 Log 两张表,两张表,在配置和不配置事务管理的情况下,两张数据库表一致性不一样。 其次需要结合 aop来使用
<!-- 配置事务增强的advice -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
		<!--配置事务的属性,通过method来进行配置-->
        <tx:attributes>
            <!-- all methods starting wit`h 'get' are read-only -->
            <tx:method name="get*" read-only="true" />
            <!-- other methods use the default transaction settings (see below) -->
            <tx:method name="*" />
        </tx:attributes>
    </tx:advice>

 <!-- 配置事务的AOP切面 --> 
	<!--<aop:config>
        <aop:pointcut id="allService" expression="execution(* edu.courseware.spring.tx.service.*Service.*(..)))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="allService"/>
    </aop:config> -->

以及隔离级别处理两个事务之间的关系,回滚 与不回滚的操作

注解配置方式

在 xml 中开始注解方式支持
<!-- 开启注解方式的事务配置支持( 注解的方式:@EnableTransactionManagement) --> 
<tx:annotation-driven transaction-manager="txManager"/>
或以注解的方式开启
@Configuration 
@ComponentScan("com.study.mike.spring.sample.tx") 
@ImportResource("classpath:com/study/mike/spring/sample/tx/application.xml") 
@EnableTransactionManagement 
public class TxMain { 
}
在要加事务管理的类或方法上加 @Transactional 注解
@Transactional(propagation = Propagation.REQUIRES_NEW) 
public void insertLog(Log log) { 
    this.logDao.insert(log);
}
掌握 @Transactional 的属性配置: rollbackFor 默认情况下是对 RuntimeException 进行回滚。

声明式事务管理

 Spring提供基于AOP的声明式事务管理,当有大量的事务需要进行管理时,声明式事务管理更合适,让 我们的事务管理变得简单、易用!

编程式事务管理

需要快速简单的事务管理时,适用编程式事务管理。

// 1、创建事务定义
		DefaultTransactionDefinition definition = new DefaultTransactionDefinition();

		// 2、根据定义开启事务
		TransactionStatus status = txManager.getTransaction(definition);

		try {
			this.userDao.insert(u);
			Log log = new Log(System.currentTimeMillis() + "", System.currentTimeMillis() + "-" + u.getUserName());
			// this.doAddUser(u);
			this.logService.insertLog(log);
			// 3、提交事务
			txManager.commit(status);
		} catch (Exception e) {
			// 4、异常了,回滚事务
			txManager.rollback(status);
			throw e;
		}
		// 在TransactionTemplate中使用的也是编程式事务管理方式

TransactionDefinition 结构 编程式事务管理简化使用 TransactionTemplate , TransactionTemplate 中的 execute 方法代码实现:

 

Spring事务管理API 

Spring 为事务管理提供了统一的抽象建模,这样我们使用 Spring 来进行事务管理时,就只需要学会这套 API即可,无论底下使用的是何种事务管理方式, jdbc 也好, jpa 也好, hibernate 也好, jta 也好。我们的业务代码中都是面向spring 的事务 API 。

TransactionDefinition

TransactionDefinition :事务定义。 Spring 事务管理框架将为我们管理事务,但不清楚该如何替我们管 理,我们就通过事务定义来定义我们需要的事务管理信息,把这些信息给事务管理器,它就知道我们的意图了。

TransactionDefinition接口的内部

ISOLATION_DEFAULT :使用的是底层(数据库)默认的隔离级别,不同的数据库默认隔离级别不同, 数据库也是可以配置这个默认属性的。 TransactionDefinition 实现体系 默认的事务配置 , 包括下面的。

TransactionAttribute 前面的事务定义中没有回滚的信息,在 TransactionAttribute 有定义

TransactionDefinition的继承体系

PlatformTransactionManager

PlatformTransactionManager 平台级的事务管理器,它抽象定义了事务管理行为,不同的事务管理实现实现该接口。我们编程面向该接口。 PlatformTransactionManager 的接口定义 接口中定义了的事务管理行为。

 PlatformTransactionManager的实现

分布式事务,则用 JTA AbstractPlatformTransactionManager 对 PlatformTransactionManager 的三个方法的实现逻辑
// 下面的三个动作都是由这个类来完成的
 getTransaction() 
commit() 
rollback()
getTransaction的流程  1. 获取事务对象 2. 如果当前存在事务,则根据事务定义中的传播行为来进行处理 3. 当前不存在事务,则根据事务定义的传播行为来决定如何处理 3.1 如果一定需要事务,而当前又不存在事务,则抛出异常 3.2 如果是下面的三种传播行为,则创建事务 挂起当前事务 开始一个新的事务 3.3 如果传播行为是默认的,使用一个空的事务,交给底层去进行处理

 

开启新的事务方法

创建一个新的同步 开始事务 初始化事务同步控制的信息

 统一控制  dao层的方法  。

 prepareSynchronization

同步器加锁

 挂起当前的事务操作

 判断事务是否活跃。

 并 返回 事务的holder  放的是事务挂起信息

 

 对于 事务活跃的情况,把相关属性装起来,作一个切换。

 这些挂起的资源 都放到新的状态里面去了

TransactionStatus

TransactionStatus 事务状态,持有事务的状态信息。事务管理代码可通过它获取事务状态、以及显式地设置回滚(代替异常的方式)。它继承了SavePoint 接口。在它的实现中会持有事务的很多对象:如事务对象、被挂起的事务资源等等。 从 TransactionManager 中获取事务得到它,提交 / 回滚事务时要给入它:

控制  事务等等,保存点。

 

 DefaultTransactionStatus

DataSourceTransactionManager

DataSourceTransactionManager 是基于 jdbc connection 的本地事务管理实现。多个方法调用参与到同一个事务,是通过共用connection 来完成的,这里面实现的标准

 获取事务,获得连接  处理事务。

断点调试看执行过程

使用的地方

AbstractPlatformTransactionManager.getTransaction  打断点看情况

getTransaction ,看调用栈 获取到连接,并做绑定起来。

 

数据源  datasource获取到连接内容 。

拿到事务   这里面就是 为后面的做的处理。

 两个service 调用了dao,放到数据库连接上。

最后 走到 开启事务的部分。

 这部分做的就是 把事务连接 放到事务对象中。datasourcetrancationmanage中的处理

 设置 自动连接的东西   以及创建事务前的处理

 这里创建好的事务将他绑定到 对应的threadlocal上面去。

 其实最终你会发现  在 事务框架中 通过threadlocal将对应的 datasource  和 连接 存到这里面做一个缓存起来。  并且  创建新事务时将 这个存到事务对象中,当然也会包括许多 前置化的处理以及属性的设置。

JdbcTemplate.execute UserDao 中是使用 JdbcTemplate 来进行的操作,找到 JdbcTemplate 的 update 操作的获取 connection 的代码,加断点,然后F8 ,让程序执行到这里,看下调用栈。

F8,JDBCTeamplate.execute的调用栈 

F5进入DataSourceUtils.getConnection(obtainDataSource()),看它如何获取Connection  

 TransactionSynchronizationManager#doGetResource代码,又回到了ThreadLocal的resources

从 ThreadLocal 获得相同的数据库连接,才能进行事务的管理和控制。 第二次进到 getTransaction UserService 和 LogService 都用了同一个 Connection ,也处于同一个事务中。 跟代码看已存在事务的处理逻辑, AbstractPlatformTransactionManager#handleExistingTransaction方法的实现。

 验证其他两种传播行为,及其他的组合情况

事务传播行为

 

 声明式事务处理过程

标签解析 TxNamespaceHandler
@Override 
public void init() { 
    registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());     
     registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser()); 
     registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser()); 
}
TxAdviceBeanDefinitionParser <tx:advice> 标签的解析器 TxAdviceBeanDefinitionParser    TxAdviceBeanDefinitionParser的关键方法

TransactionInterceptor TransactionInterceptor 就是一个环绕织入 MethodInterceptor 的实现
  • 浏览TransactionInterceptor的代码,重点:invoke(MethodInvocation invocation)方法 invokeWithinTransaction方法

  •  浏览TransactionInterceptor的继承体系,事务处理的逻辑都在TransactionAspectSupport中

  • 浏览TransactionAspectSupport,它里面有如下属性

 

它的重点方法是 invokeWithinTransaction TransactionAttributeSource
  • 浏览TransactionAttributeSource接口定义

  • 浏览TransactionAttributeSource 的继承体系  

 

 切面增强过程

TransactionAspectSupport.invokeWithinTransaction 方法 从代码中可看出:标准的编程式事务管理流程
  • 获得TransactionAttributeSource
  • 获得事务定义TransactionAttribute
  • 获得TransactionManager
  • 如果有对应的事务定义并且事务管理器是ReactiveTransactionManager类型的,进行响应的 处理
  • 如果没有对应的事务定义,或者事务管理器不是

对于编程式事务的一个封装。

事务监听

Data Access (spring.io)

从 Spring 4.2 开始,事件的监听器可以绑定到事务的一个阶段。典型的例子是在事务成功完成时处理事件。当当前事务的结果实际上对侦听器很重要时,这样做可以更灵活地使用事件。

您可以使用 @EventListener 注释注册常规事件侦听器。如果需要将其绑定到事务,请使用@TransactionalEventListener。当您这样做时,默认情况下侦听器绑定到事务的提交阶段。 下一个示例显示了这个概念。假设一个组件发布了一个订单创建的事件,并且我们想要定义一个侦听器,该侦听器仅在发布它的事务成功提交后才处理该事件。以下示例设置了这样一个事件侦听器:

本质上就是一个EventListener,类似继承 @TransactionalEventListener
@Component
public class MyComponent {

    @TransactionalEventListener
    public void handleOrderCreatedEvent(CreationEvent<Order> creationEvent) {
        // ...
    }
}

 触发事件监听的一个注解

 @TransactionalEventListener 注释公开了一个阶段属性,该属性允许您自定义侦听器应绑定到的事务的阶段。有效阶段是 BEFORE_COMMIT、AFTER_COMMIT(默认)、AFTER_ROLLBACK 和 AFTER_COMPLETION,

它们聚合事务完成(无论是提交还是回滚)。 如果没有事务正在运行,则根本不会调用侦听器,因为我们无法遵守所需的语义。但是,您可以通过将注解的 fallbackExecution 属性设置为 true 来覆盖该行为。

Spring 中的事件发布订阅机制 整体而言 Spring 的事件机制是通过发布订阅来达到的。

 

它需要注册一个事件监听处理器, EventListenerMethodProcessor 就是用来处理事件方法监听,只不过最终使用 TransactionalEventListenerFactory 生成一个 Adapter 适配器。

 

 注解的扫描起来,进行存储起来。

ApplicationListenerMethodAdapter 下面有一个 ApplicationListenerMethodTransactionalAdapter 类,用来处理事务监听器的。 AbstractPlatformTransactionManager#triggerAfterCommit TransactionSynchronizationUtils#invokeAfterCompletion AbstractPlatformTransactionManager#triggerBeforeCommit TransactionSynchronizationUtils#triggerBeforeCommit after 事件触发

 事件发布的堆栈

自定义注解

 @Import注解

 通过@Import导入一个或多个@link Configuration类   

4.2后,三种情况:@Configuration、ImportSelector、ImportBeanDefinitionRegistrar 的实现会被IOC注册

引入的类都可以作为 component 注册到ioc中

在spring中对于 import 的  解析  是在  ContextNamespaceHandler 中

 

 AnnotationConfigBeanDefinitionParser 跟着进去

 对于 import 注解来说 ConfigurationClassPostProcessor   是在这里做的处理的 

ConfigurationClassPostProcessor#processConfigBeanDefinitions

 ConfigurationClassParser.processImports(…)方法

 

最后放到configclass 以及 importstack 中进行 导入进去。

注解用来做配置,简化xml的配置信息。

分布式事务JTA 

 分布式事务  具有多个数据源

 

一个事务包含多个操作,多个操作操作了多个数据源,这样的事务称为分布式事务。 与普通事务的区别 普通事务操作,一个单一数据源事务 单一数据源,事务管理可以借助数据源本地事务完成,实现简单! 分布式事务管理之困难:不可简单借助数据源本地事务完成! 一个分布式事务示例

 尝试借助本地事务来完成事务管理

分布式事务管理需要什么 分布式事务管理需要的机制 1. 协调各数据源提交、回滚,及应对通信异常的管理机制。 2. 数据源需要支持这种机制。 3. 应对应用故障恢复的机制。

 从上面得出,做分布式事务管理需要的参与者

 

XA 规范 什么是 XA 规范

 XA规范是X/Open(The open group)提出的分布式事务处理规范,分布式事务处理的工业标准。

 

 

XA- 两阶段提交 第一阶段:准备阶段

第二阶段:提交/回滚

 JTA

JTA: Java Transaction API JAVA 根据 XA 规范提出的事务处理 API 标准 目的:统一 API, 简化程序员的学习,简化编程

 javax下的jar包

<dependency>
    <groupId>javax.transaction</groupId>
    <artifactId>javax.transaction-api</artifactId>
    <version>1.3</version>
</dependency>

JTA-API 构成 面向 TM RM 提供商的 API  TransactionManager  Transaction  XARsource  Xid TM 实现提供商 JavaEE 应用服务器内建 JTA 事务管理( TM ),提供商:
  • Weblogic
  • Websphere

 开源、独立的JTA事务管理器(TM)组件:

  • Java Open Transaction Manager (JOTM)
  • JBoss TS
  • Bitronix Transaction Manager (BTM)
  • Atomikos
  • Narayana
RM 实现提供商 一般数据库厂商会提供实现 在连接池组件中一般也会提供包装实现:

Spring中应用JTA

Spring 中的 JTA
  • Spring自身并不提供jta TM实现,但提供了很好的集成
  • 根据TM的提供者不同,分为两种应用方式:
方式一:使用 javaEE 服务器内建的 TM 方式二:在没有实现 TM 的应用服务器上( Tomcat,jetty ),将独立 TM 组件集成到我们的 spring 应 用中 使用 JavaEE 服务器内建 TM 用法:做如下配置即可 xml 配置
<tx:jta-transaction-manager />
或者 Java 配置
@Bean
public PlatformTransactionManager platformTransactionManager(){
    return new JtaTransactionManager();
}
说明: JtaTransactionManager 通过 JNDI 找到服务器提供的 java:comp/UserTransaction, java:comp/TransactionManager 应用使用的数据源需是支持 xa 的数据源。 使用轻量级服务器 + 集成 TM 组件 操作步骤: 1. 引入 TM 组件的 jar (以 Atomikos 为例) Spring 应用方式
<!-- jta api -->
<dependency>
    <groupId>javax.transaction</groupId>
    <artifactId>javax.transaction-api</artifactId>
    <version>1.3</version>
</dependency>
<!-- atomikos 数据库的TM组件 -->
<dependency>
    <groupId>com.atomikos</groupId>
    <artifactId>transactions-jdbc</artifactId>
    <version>4.0.6</version>
</dependency>
<!-- atomikos JMS 有MQ需要事务管理时加入 -->
<dependency>
    <groupId>com.atomikos</groupId>
    <artifactId>transactions-jms</artifactId>
    <version>4.0.6</version>
</dependency>
Spring Boot Starter 方式
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jta-atomikos</artifactId>
    <version>2.1.1.RELEASE</version>
</dependency>
配置数据源,一定要是 XA 数据源 准备两个数据源 properties 配置数据源
# jdbc properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=root

db1.jdbc.url=jdbc:mysql://127.0.0.1:3306/test?
useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
db2.jdbc.url=jdbc:mysql://127.0.0.1:3306/logdb?
useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC

Spring XML配置XA DataSource

<!-- 加载配置参数 -->
<context:property-placeholder
location="classpath:com/study/mike/spring/sample/jta/application.properties"/>
<!-- xa数据源1 -->
<bean id="db1DataSource“ class="com.atomikos.jdbc.AtomikosDataSourceBean "init-method="init" destroy-method="close">
<!-- 给数据源取个唯一区分的名称 -->
<property name="uniqueResourceName" value="mysql/db01" />
<!-- 真正使用的 XA DataSource 类名 -->
    <property name="xaDataSourceClassName"value="com.alibaba.druid.pool.xa.DruidXADataSource" />
<!-- 数据源连接相关配置参数 -->
   <property name="xaProperties">
    <props>
        <prop key="url">${db1.jdbc.url}</prop>
        <prop key="username">${jdbc.username}</prop>
        <prop key="password">${jdbc.password}</prop>
    </props>
   </property>
</bean>
<!-- xa数据源2 -->
<bean id="db2DataSource“ class="com.atomikos.jdbc.AtomikosDataSourceBean“
init-method="init" destroy-method="close">
……
</bean>
配置事务管理器 TM 1. TransactionManager 的实现 bean 2. UserTransaction 的实现 bean 3. spring 的 JtaTransactionManager (注入 TM 、 UserTransaction ) 如果是在 spring-boot 中, 用 spring-boot-starter-jta-atomikos ,这步会自动配置好,不需手动配 置! Spring 应用 JTA 使用 jta 需满足: 1. 数据源支持分布式事务 2. 要有 jta 的实现提供者( javaEE 服务器内建的或独立实现组件) 3. Spring 中配置使用 JtaTransactionManager 来处理事务
这篇关于Spring中事务源码解读的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!