前言
之前的文章是解析spring中ioc源码 以及 aop源码 ,包括核心的bean的生命周期 以及 各个扩展部分,以及 aop源码 如何开启注解时, 解析注解标签时,将 所有 aop所拥有的控件在bean实例化 之前 和实例化之后的一个 扩展 AnnotationAwareAspectJAutoProxyCreator 这个类上 面做的所有的处理和扩展。本篇文章会继续 研究 事务源码部分, 包括事务隔离级别,以及 事务如何实现的。
Spring事务管理
Spring 框架为事务管理提供一个一套统一的抽象,带来的好处有: 1. 跨不同事务 API 的统一的编程模型,无论你使用的是 jdbc 、 jta 、 jpa 、 hibernate 。 2. 支持声明式事务 3. 简单的事务管理 API 4. 能与 spring 的数据访问抽象层完美集成Spring框架的事务支持模型的优势
传统上,Java EE 开发人员对事务管理有两种选择:全局事务或本地事务,这两者都有很大的局限性。接下来的两节将回顾全局和本地事务管理,然后讨论Spring框架的事务管理支持如何解决全局和本地事务模型的局限性。
事务概念
Isolation 隔离级别 此事务与其他事务的工作隔离的程度。例如,该事务能否看到来自其他事务的未提交的写操作- READ_UNCOMMITTED 读未提交
- READ_COMMITTED 读已提交
- REPEATABLE_READ 可重复读
- SERIALIZABLE 序列化(串行)
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类,查看数据库操作结果
<!-- 配置事务增强的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类型的,进行响应的 处理
- 如果没有对应的事务定义,或者事务管理器不是
对于编程式事务的一个封装。
事务监听
从 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
Spring中应用JTA
Spring 中的 JTA- Spring自身并不提供jta TM实现,但提供了很好的集成
- 根据TM的提供者不同,分为两种应用方式:
<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
来处理事务