文章目录
Spring深度学习:事务实现源码解析
一、序言
大家经常戏称Java工程师为Spring工程师,毫无疑问,这句话体现出Spring框架在Java开发中的重要性和普及度。
本文小豪将带大家深度学习Spring事务相关知识,包括Spring对数据库事务控制的组装过程底层源码,以及编程式事务和声明式事务两种实现方法源码解析,深入剖析Spring处理事务的管理策略,认识Spring对于多种事务传播行为的处理方法,提升我们的架构设计能力。
文章最后附有流程图,进一步帮我们梳理业务逻辑
二、前置知识
1、事务
事务,想必大家也耳熟能详了,小豪再带大家简略复习一遍。
事务即一段不可分割的逻辑单元,要么完全执行,要么完全不执行,在数据库设计中用来保证数据库的完整性和一致性,执行时遵循ACID特性原则:
- 原子性(Atomicity):事务是数据库操作的最小单位,意味着操作要么全部执行,要么全部不执行。
- 一致性(Consistency):事务的执行应保持数据库的一致性状态,即事务的执行不会违反数据的完整性约束。
- 隔离性(Isolation):事务的执行不应相互干扰,即一个事务的执行不应影响其他并发事务的执行。
- 持久性(Durability):一旦事务提交,其对数据库的更改应该是永久性的,即使系统发生故障也不会丢失。
2、Spring事务
而Spring事务,本质上依赖数据库对事务的支持。基于数据库事务,Spring将它们封装起来,提供一些更为便捷的功能,帮助我们更好的控制、管理事务。
在学习Spring事务源码之前,先介绍一下Spring事务中比较关键的三个接口和一个类:
本文部分粘贴源码已做精简处理,删除了不重要的日志打印、异常捕获等
(1)PlatformTransactionManager事务管理器
Spring事务处理的核心接口,声明了事务处理的三个核心方法:获取事务、提交事务和回滚事务
public interface PlatformTransactionManager {
// 方法一:根据TransactionDefinition事务配置定义,获取TransactionStatus事务运行状态
TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
// 方法二:提交事务
void commit(TransactionStatus status) throws TransactionException;
// 方法三:回滚事务
void rollback(TransactionStatus status) throws TransactionException;
}
下图是PlatformTransactionManager
接口的组件关系,其中AbstractPlatformTransactionManager
是它的抽象实现,而常见的DataSourceTransactionManager
是JDBC的具体实现类:
(2)TransactionDefinition事务属性定义
定义事务的基本属性的顶层接口,包括事务传播行为、隔离级别、超时时间、是否只读:
public interface TransactionDefinition {
// 事务传播行为
int PROPAGATION_REQUIRED = 0;
int PROPAGATION_SUPPORTS = 1;
int PROPAGATION_MANDATORY = 2;
int PROPAGATION_REQUIRES_NEW = 3;
int PROPAGATION_NOT_SUPPORTED = 4;
int PROPAGATION_NEVER = 5;
int PROPAGATION_NESTED = 6;
// 事务隔离级别
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
// 事务超时时间
int TIMEOUT_DEFAULT = -1;
// 获取事务传播行为
int getPropagationBehavior();
// 获取事务隔离级别
int getIsolationLevel();
// 获取事务超时时间
int getTimeout();
// 事务是否只读
boolean isReadOnly();
@Nullable
String getName();
}
这里引出了事务的传播行为,额外介绍一下:
事务的传播行为一般用在事务嵌套的场景,在一般软件程序设计的三层架构中,我们的业务通常都会写在Service层,比如现在有两个Service层:ServicA
、ServicB
。
ServicA类:
public class ServiceA implements IServiceA {
@Autowired
ServiceB serviceB;
@Transactional
public void methodA(){
// 数据库添加A信息
insertAInfo();
// 调用serviceB的methodB()方法,数据库删除B信息
serviceB.methodB();
}
}
ServicB类:
public class ServiceB implements IServiceB{
@Transactional
public void methodB(){
// 数据库删除B信息
deleteBInfo();
}
}
其中ServicA
层的方法A调用了另一个ServicB
层的方法B,如果方法A、B都存在事务的话,我们应该采用什么规则去处理这种事务的传播行为,Spring提供的事务传播行为配置一共有7种类型:
事务传播行为 | 描述 |
---|---|
PROPAGATION_REQUIRED | Spring默认的传播行为,表示需要运行于事务,如果外层不存在事务,则自己新开启一个事务执行,如果外层已经存在事务,则加入到外层事务 |
PROPAGATION_REQUIRES_NEW | 表示必须运行于自己的事务,每次都会新开启一个事务,如果外层已经存在事务,把外层事务挂起,等当前事务执行完毕后,再恢复外层事务 |
PROPAGATION_MANDATORY | 表示必须运行于事务,如果外层不存在事务,则直接抛出异常,不会开启新的事务,如果外层已经存在事务,则加入到外层事务 |
PROPAGATION_SUPPORTS | 表示支持运行于外层事务,自身使用非事务方式运行,如果外层已经存在事务,则加入到外层事务 |
PROPAGATION_NOT_SUPPORTED | 表示不支持运行于外层事务,始终使用非事务方式运行,如果外层已经存在事务,把外层事务挂起 |
PROPAGATION_NEVER | 表示不能运行于事务,始终使用非事务方式运行,如果外层已经存在事务,则直接抛出异常 |
PROPAGATION_NESTED | 表示运行于嵌套事务,如果外层不存在事务,则自己新开启一个事务执行,如果外层已经存在事务,则会使用嵌套事务执行 |
其中PROPAGATION_NESTED嵌套事务概念比较特殊,即内部嵌套事务不能独立提交,其依赖于外部事务,外部事务提交则内部嵌套事务同时提交,外部事务回滚则内部嵌套事务回滚,而内部嵌套事务回滚不会引起外部事务回滚,只会回滚自身
(3)TransactionStatus事务运行状态
定义事务的运行状态的接口,包括判断事务是否为新事务、是否存在保存点、是否完成等:
public interface TransactionStatus extends SavepointManager, Flushable {
// 是否属于新事务
boolean isNewTransaction();
// 是否存在保存点
boolean hasSavepoint();
// 设置只回滚
void setRollbackOnly();
// 是否只回滚
boolean isRollbackOnly();
// 刷新至数据库
@Override
void flush();
// 是否已完成
boolean isCompleted();
}
(4)TransactionSynchronizationManager事务同步管理器
顾名思义与事务同步有关,一般来说我们进行数据库操作时,每次都会获取一个connection
数据库连接资源,这显然违背了事务的ACID特性原则,事务控制的数据库操作必须要作为一个整体,则多个数据库操作需要使用同一个connection
数据库连接。
因此在TransactionSynchronizationManager
事务同步管理器中,通过ThreadLocal
来管理数据库连接资源,为不同的线程提供独立的资源副本。
public abstract class TransactionSynchronizationManager {
private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);
// 保存当前线程的连接资源
// key(DataSource不同数据源) -> value(Connection连接)
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
// 保存当前线程的事务同步器(实现TransactionSynchronization接口,Spring提供的扩展点)
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
// 保存当前线程的事务名称
private static final ThreadLocal<String> currentTransactionName =
new NamedThreadLocal<>("Current transaction name");
// 保存当前线程的事务是否只读
private static final ThreadLocal<Boolean> currentTransactionReadOnly =
new NamedThreadLocal<>("Current transaction read-only status");
// 保存当前线程的事务隔离级别
private static final ThreadLocal<Integer> currentTransactionIsolationLevel =
new NamedThreadLocal<>("Current transaction isolation level");
// 保存当前线程的事务的活跃状态
private static final ThreadLocal<Boolean> actualTransactionActive =
new NamedThreadLocal<>("Actual transaction active");
// 获取当前线程的所有连接资源
public static Map<Object, Object> getResourceMap() {
Map<Object, Object> map = resources.get();
return (map != null ? Collections.unmodifiableMap(map) : Collections.emptyMap());
}
// 检查当前线程是否有指定的数据库连接
public static boolean hasResource(Object key) {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Object value = doGetResource(actualKey);
return (value != null);
}
// 获取当前线程中指定的数据库连接
@Nullable
public static Object getResource(Object key) {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Object value = doGetResource(actualKey);
return value;
}
// 通过key(DataSource数据源),查找value(Connection连接)
@Nullable
private static Object doGetResource(Object actualKey) {
Map<Object, Object> map = resources.get();
if (map == null) {
return null;
}
// 如果存在
Object value = map.get(actualKey);
// 标记此连接资源并从当前线程中删除
if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
map.remove(actualKey);
if (map.isEmpty()) {
resources.remove();
}
value = null;
}
return value;
}
// 绑定数据源和数据库连接到当前线程
public static void bindResource(Object key, Object value) throws IllegalStateException {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Map<Object, Object> map = resources.get();
// 当前Map不存在,定义ThreadLocal Map
if (map == null) {
map = new HashMap<>();
resources.set(map);
}
// 绑定当前传入的连接资源,如果存在旧的连接资源,标记为无效
Object oldValue = map.put(actualKey, value);
if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {
oldValue = null;
}
}
// 从当前线程中删除指定的连接资源
public static Object unbindResource(Object key) throws IllegalStateException {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Object value = doUnbindResource(actualKey);
return value;
}
}
源码中逻辑也比较简单,数据库连接资源的ThreadLocal
为Map
结构,键存放不同的数据源DataSource
,对应的值存放connection
数据库连接对象。
其中的获取数据库连接资源、绑定数据库连接资源、删除数据库连接资源本质上均是对此ThreadLocalMap
的维护操作。
三、编程式事务
编程式事务即使用相关代码控制事务,相对来说更为灵活,但需要手动实现,较为繁琐。
编程式事务另一方面可以规避声明式事务部分情况下造成的事务失效问题
声明式事务为何可能造成事务失效呢?下面会带大家解读
接下来我们先编写一个编程式事务控制逻辑,再带大家剖析其底层源码。
1、基础环境准备
XML配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring_study?serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--配置JDBC模板-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置UserService,注入JDBC模板和事务管理器-->
<bean id="userService" class="org.springframework.jdbc.programtransaction.UserService">
<constructor-arg ref="transactionManager"/>
<constructor-arg ref="jdbcTemplate"/>
</bean>
</beans>
编程式事务代码:
public class UserService {
private PlatformTransactionManager transactionManager;
private JdbcTemplate jdbcTemplate;
// 构造方法注入事务管理器、JDBC模板
public UserService(PlatformTransactionManager transactionManager, JdbcTemplate jdbcTemplate) {
this.transactionManager = transactionManager;
this.jdbcTemplate = jdbcTemplate;
}
public void updateUser() {
// 默认的事务配置定义
TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
// 使用事务管理器获取事务状态
TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
try {
// 执行数据库操作
jdbcTemplate.update("insert into user value(?,?,?) ", 1, "xiaohao", 24);
jdbcTemplate.update("update user set age = ? where name = ? ", 25, "xiaohao");
} catch (Exception e) {
// 产生异常,回滚事务
transactionManager.rollback(transactionStatus);
}
// 正常执行,提交事务
transactionManager.commit(transactionStatus);
}
}
测试类:
public class SpringDemo {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-config.xml");
UserService user = (UserService) applicationContext.getBean("userService");
user.updateUser();
}
}
不出意外,这里将会正常在user表中插入一条用户数据,然后修改其用户年龄。
接下来我们详细分析一下PlatformTransactionManager
事务管理器的三个核心方法:
- transactionManager.getTransaction(transactionDefinition):获取事务
- transactionManager.commit(transactionStatus):提交事务
- transactionManager.rollback(transactionStatus):回滚事务
2、获取事务getTransaction()
上文中我们知道PlatformTransactionManager
接口的抽象实现类是AbstractPlatformTransactionManager
,直接进入AbstractPlatformTransactionManager
抽象类的getTransaction()
方法:
部分粘贴源码有做删减,去除冗杂信息
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
// 获取事务
Object transaction = doGetTransaction();
boolean debugEnabled = logger.isDebugEnabled();
// 判断传入的事务配置是否为空(自定义隔离级别,传播行为等)
if (definition == null) {
// 设置默认的事务配置
definition = new DefaultTransactionDefinition();
}
// 判断当前线程是否已存在事务(当前线程的事务存在数据库连接connection,且为活跃状态)
if (isExistingTransaction(transaction)) {
// 已存在外部事务,处理内部嵌套事务
return handleExistingTransaction(definition, transaction, debugEnabled);
}
// 此时表示当前线程不存在事务,根据传播行为执行相应逻辑
// 判断新事务的超时设置
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
// 传播行为 PROPAGATION_MANDATORY
// 表示必须在事务中运行,但当前线程不存在事务,则直接抛出异常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
// 传播行为 PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED
// 新建一个事务
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 由于当前线程不存在事务,则挂起一个空事务
SuspendedResourcesHolder suspendedResources = suspend(null);
try {
// 开启新的事务
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// (核心)构造transaction
doBegin(transaction, definition);
// 设置新的同步事务到ThreadLocal
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
else {
// 其它的传播行为 PROPAGATION_SUPPORTS、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER
// 创建一个空事务,即没有实际的事务
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + definition);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}
观察源码发现,getTransaction()
方法大致逻辑如下:
-
获取事务: 执行
doGetTransaction()
方法来获取当前线程中的数据库连接资源 -
设置事务配置定义:判断是否传入了事务配置定义,如果为空则设置一个默认的事务配置
-
检查当前是否存在事务: 调用
isExistingTransaction()
方法判断当前是否存在正在进行的事务。- 当前存在事务:调用
handleExistingTransaction()
方法处理内部嵌套事务 - 当前不存在事务:首先判断事务是否超时,之后根据
TransactionDefinition
事务配置定义设置的事务传播行为执行不同的逻辑,其中REQUIRED、REQUIRES_NEW和NESTED三种传播行为将会调用doBegin()
方法新建一个事务去执行
- 当前存在事务:调用
我们进一步查看里面几个比较重要的方法:
- 获取事务
doGetTransaction()
- 处理内部嵌套事务
handleExistingTransaction()
- 挂起事务
suspend()
- 开启事务
doBegin()
(1)doGetTransaction
这里我们进入DataSourceTransactionManager
实现类的doGetTransaction()
方法,主要是用来获取事务,具体源码如下:
protected Object doGetTransaction() {
// 创建新的事务
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
// 是否允许设置保存点,即是否允许嵌套事务
txObject.setSavepointAllowed(isNestedTransactionAllowed());
// 获取连接资源,如果当前线程已存在数据库连接则直接使用
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
// 设置数据库连接
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
在源码中我们看到,doGetTransaction()
方法主要是创建了一个新的事务对象,并为其设置属性,包括设置是否允许保存点、设置数据库连接。
其中获取数据库连接资源则通过我们上文介绍的TransactionSynchronizationManager
事务同步管理器中的getResource()
方法,本质上是通过数据源拿到当前线程ThreadLocal
中对应的connection
连接。
(2)handleExistingTransaction
用于当前已经存在事务时,处理内部嵌套事务,回到AbstractPlatformTransactionManager
抽象类的handleExistingTransaction
方法中,源码如下:
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
// 传播行为 PROPAGATION_NEVER,当前存在外部事务,直接抛异常
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
// 传播行为 PROPAGATION_NOT_SUPPORTED
// 表示不使用事务,如果当前存在外部事务,会将当前事务挂起
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
// 挂起外部事务
Object suspendedResources = suspend(transaction);
// 创建一个空事务,即没有实际的事务
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
// 新创建的空事务会存有当前已经挂起的事务资源
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
// 传播行为 PROPAGATION_REQUIRES_NEW
// 表示新创建一个事务,挂起当前事务
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
// 挂起外部事务
SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
// 开启新的事务,新事务存有当前已经挂起的事务资源
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException | Error beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
}
// 传播行为 PROPAGATION_NESTED
// 表示使用嵌套事务
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
// 判断是否支持使用savepoint保存点来处理嵌套事务
if (useSavepointForNestedTransaction()) {
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
// 创建保存点
status.createAndHoldSavepoint();
return status;
}
else {
// 不支持则直接开启新的事务
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, null);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}
// 其它的传播行为 PROPAGATION_REQUIRED、PROPAGATION_SUPPORTS、PROPAGATION_MANDATORY
// 创建一个空事务,此处则使用了外部事务
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
查看handleExistingTransaction()
方法,内部的逻辑也比较清晰,总结一下:
在当前已经存在外部事务的前提下,进一步根据事务传播行为走不同的处理逻辑。比较典型的,在外部有事务的情况下,其中REQUIRES_NEW会创建一个新事务,同时挂起当前外部的事务。而REQUIRED、SUPPORTS、MANDATORY则会直接使用外部事务。
(3)suspend
用于挂起事务,进入AbstractPlatformTransactionManager
抽象类的suspend()
方法,具体源码如下:
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
// 判断当前事务是否为活跃状态
if (TransactionSynchronizationManager.isSynchronizationActive()) {
List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
try {
Object suspendedResources = null;
if (transaction != null) {
// 解绑当前事务的数据库连接connection
suspendedResources = doSuspend(transaction);
}
// 将当前事务的信息设置为空或false
String name = TransactionSynchronizationManager.getCurrentTransactionName();
TransactionSynchronizationManager.setCurrentTransactionName(null);
boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);
Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);
boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();
TransactionSynchronizationManager.setActualTransactionActive(false);
return new SuspendedResourcesHolder(
suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive);
}
catch (RuntimeException | Error ex) {
doResumeSynchronization(suspendedSynchronizations);
throw ex;
}
}
// 当前事务不为活跃状态
else if (transaction != null) {
Object suspendedResources = doSuspend(transaction);
return new SuspendedResourcesHolder(suspendedResources);
}
else {
return null;
}
}
观察源码发现,suspend()
方法主要是临时存储当前存有的外部事务,同时将当前的外部事务解绑(在当前线程ThreadLocal
中移除对应的数据连接资源),并且清除外部事务的状态信息,将其封装为SuspendedResourcesHolder
对象并返回。
这里封装的SuspendedResourcesHolder
对象会继续传递给新创建的内部事务,当新事务执行完毕之后,将会恢复当前的外部事务。
(4)doBegin
最后我们进入doBegin()
方法,它是实现事务开启的核心,具体源码如下:
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
// 判断当前线程的数据库连接不存在 or 事务同步
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
// 从数据源datasource中获取一个新的数据库连接connection
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
// 设置新的数据库连接connection
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
// 设置事务同步标识
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
// 取出新的数据库连接connection
con = txObject.getConnectionHolder().getConnection();
// 设置事务隔离级别
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
// (Spring控制事务的本质)修改自动提交为false
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
// 关闭数据库的自动提交
con.setAutoCommit(false);
}
// 事务连接准备
prepareTransactionalConnection(con, definition);
// 设置事务为活跃状态
txObject.getConnectionHolder().setTransactionActive(true);
// 设置超时时间
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// 判断是否为新的数据库连接
if (txObject.isNewConnectionHolder()) {
// 绑定当前的数据库连接资源到当前线程
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
观察源码发现,doBegin()
方法大致做了如下工作:
- 获取数据库连接:首先判断当前线程的
connection
数据库连接是否存在或事务同步为true,若满足则会获取新的连接 - 设置同步标识及隔离级别:进一步设置当前事务的同步标识以及事务隔离级别
- 修改数据库自动提交:关闭数据库的自动提交,交由Spring控制
- 设置事务活跃状态及超时时间:做一些准备工作,同时设置事务为活跃状态并且设置其超时时间
- 绑定连接资源到当前线程:将
connection
数据库连接绑定到当前线程的ThreadLocal
综上分析,doBegin()
方法将会获得当前线程的connection
数据库连接,同时设置相关的事务属性,关闭数据库的自动提交,最终再将connection
注册到ThreadLocal
缓存中。
看到这里,想必大家也理解了Spring事务的核心实现,没错,本质上依赖于数据库本身对事务的支持。Spring通过关闭数据库的自动提交(autocommit),将操作交由Spring自身来完成对事务的控制。
3、提交事务commit()
接下来我们查看提交事务,进入AbstractPlatformTransactionManager
抽象类的commit()
方法,具体源码如下:
public final void commit(TransactionStatus status) throws TransactionException {
// 事务是否已完成
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
// 判断事务是否已被标记回滚,直接进行回滚。
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isLocalRollbackOnly()) {
processRollback(defStatus, false);
return;
}
// 判断是否已设置为全局回滚
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
processRollback(defStatus, true);
return;
}
// 执行事务提交
processCommit(defStatus);
}
源码中,逻辑也比较简单,首先进行一系列检查,判断事务是否已完成,是否已被标记回滚,如果事务均没有任务异常,则会调用processCommit()
方法,执行事务提交。
继续进入processCommit()
方法:
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
prepareForCommit(status);
// 触发自定义实现的事务同步器,Spring提供的扩展点
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
// 判断是否设置保存点信息
if (status.hasSavepoint()) {
unexpectedRollback = status.isGlobalRollbackOnly();
// 释放保存点信息
status.releaseHeldSavepoint();
}
// 判断如果是新开始的事务
else if (status.isNewTransaction()) {
unexpectedRollback = status.isGlobalRollbackOnly();
// 新创建的独立事务直接提交
doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException | Error ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
// 在提交过程产生异常则执行回滚
doRollbackOnCommitException(status, ex);
throw ex;
}
try {
// 触发自定义实现的事务同步器,Spring提供的扩展点
triggerAfterCommit(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
// 清除事务信息
cleanupAfterCompletion(status);
}
}
观察源码发现,processCommit()
方法首先会调用我们自定义实现的事务同步器,可进行扩展,在执行提交时又分为几种情况:
- 当前事务存在保存点信息不执行提交
- 当前事务不是新开启的事务不执行提交(嵌套事务中部分情况下,内部事务沿用外部的事务,内部事务执行完不应单独提交,而需要等待外部事务进行提交)
- 当前事务是新开启事务将会执行提交
这里再详细看一下其中两个重要方法,doCommit()
和cleanupAfterCompletion()
(1)doCommit
方法内部直接调用数据库连接connection
的commit
,完成提交
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
try {
// 调用数据库连接commit方法
con.commit();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
}
(2)cleanupAfterCompletion
清除事务信息,包括标志事务已完成,清除事务同步状态,解绑当前线程的数据库连接资源,恢复数据库自动提交,恢复挂起的外部事务
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
// 设置事务已完成
status.setCompleted();
// 判断是否为新同步
if (status.isNewSynchronization()) {
// 清除当前线程中的事务同步状态,删除对应的ThreadLocal
TransactionSynchronizationManager.clear();
}
// 判断是否为新事务
if (status.isNewTransaction()) {
// 进一步清除事务,解绑当前线程的数据库连接资源,恢复数据库本身的自动提交(autocommit),重置事务隔离级别、是否只读等属性
doCleanupAfterCompletion(status.getTransaction());
}
// 判断是否存在外部挂起的事务
if (status.getSuspendedResources() != null) {
// 恢复之前挂起的外部事务
Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
}
}
4、回滚事务rollback()
最后进入AbstractPlatformTransactionManager
抽象类的rollback()
方法,具体源码如下:
public final void rollback(TransactionStatus status) throws TransactionException {
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
// 执行事务回滚
processRollback(defStatus, false);
}
继续往下走,processRollback()
方法:
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
// 扩展点
triggerBeforeCompletion(status);
// 判断是否设置保存点信息
if (status.hasSavepoint()) {
// 回滚至保存点
status.rollbackToHeldSavepoint();
}
// 判断如果是新开始的事务
else if (status.isNewTransaction()) {
// 新创建的独立事务直接回滚
doRollback(status);
}
else {
if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
doSetRollbackOnly(status);
}
}
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
}
catch (RuntimeException | Error ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
// 扩展点
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
}
finally {
// 清除事务信息
cleanupAfterCompletion(status);
}
}
源码中processRollback()
方法大致逻辑与processCommit()
方法类似,触发扩展点,分情况执行回滚,同样最后也会清除当前的事务信息。
回滚情况简单总结一下:
- 当前事务存在保存点则回滚至保存点(嵌套事务中内部事务回滚不会造成外部事务回滚)
- 当前事务是新开启的事务直接执行回滚
- 当前事务不是新开启的事务不执行回滚(设置回滚标识)
详细看一下doRollback()
方法
(1)doRollback
同样的,也是直接调用数据库连接connection
的rollback
,完成回滚
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
try {
// 调用数据库连接rollback方法
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
四、声明式事务
声明式事务的实现主要是基于AOP,即面向切面,在学习声明式事务底层源码之前,需对Spring AOP有一定了解,大家先自行学习小豪之前写的两篇文章:
声明式事务现在主流一般使用@Transactional
注解作用于类或方法上,最大程度的便捷开发,使用纯XML配置文件声明事务已经不常用了。
1、基础环境修改
为实现声明式事务,我们基于编程式事务的基础环境,改造一下环境:
修改XML配置文件:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--开启Transactional注解-->
<tx:annotation-driven />
主要做了两个修改:
- 添加了事务的命名空间URL
- 开启
@Transactional
注解事务
编程式事务代码:
public class UserService {
private PlatformTransactionManager transactionManager;
private JdbcTemplate jdbcTemplate;
// 构造方法注入事务管理器、JDBC模板
public UserService(PlatformTransactionManager transactionManager, JdbcTemplate jdbcTemplate) {
this.transactionManager = transactionManager;
this.jdbcTemplate = jdbcTemplate;
}
// 方法添加@Transactional注解、删除编程式事务代码
@Transactional
public void updateUser() {
// 执行数据库操作
jdbcTemplate.update("insert into user value(?,?,?) ", 1, "xiaohao", 24);
jdbcTemplate.update("update user set age = ? where name = ? ", 25, "xiaohao");
// 模拟异常
int i = 1 / 0;
}
}
经过测试,发现updateUser()
方法在抛出运算异常后,执行的数据库操作将会触发回滚
2、注册事务自动代理创建器及拦截器
Spring实现声明式事务本质上依赖于AOP,流程与Spring AOP很相似,我们之前已经很细致的剖析过Spring AOP及动态代理底层源码实现,对应大多数流程相信大家能一眼反应过来。
老样子,Spring IOC容器在扫描xml配置文件时,解析到我们配置的事务命名空间URL,将会加载spring.handlers
配置文件,获取到对应的全限定类名:
http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler
然后将会通过反射创建对应的TxNamespaceHandler
事务命名空间解析类:
public class TxNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
// 注册事务解析器
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
}
}
创建事务命名空间解析类时,调用其init()
方法,注册了事务注解驱动解析器AnnotationDrivenBeanDefinitionParser
对象。接着调用事务解析器的parse()
方法解析<tx:annotation-driven />
标签,获取其mode
属性,这里默认mode = proxy
:
public BeanDefinition parse(Element element, ParserContext parserContext) {
registerTransactionalEventListenerFactory(parserContext);
String mode = element.getAttribute("mode");
if ("aspectj".equals(mode)) {
// mode="aspectj"
registerTransactionAspect(element, parserContext);
}
else {
// 默认事务代理方式:mode="proxy"
AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
}
return null;
}
进入默认事务代理configureAutoProxyCreator()
方法内部:
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
// 注册事务自动代理创建器
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;
if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {
Object eleSource = parserContext.extractSource(element);
// 创建TransactionAttributeSource的BeanDefinition
RootBeanDefinition sourceDef = new RootBeanDefinition(
"org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
sourceDef.setSource(eleSource);
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
// 创建TransactionInterceptor的BeanDefinition
RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
interceptorDef.setSource(eleSource);
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registerTransactionManager(element, interceptorDef);
interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
// 创建TransactionAttributeSourceAdvisor的BeanDefinition
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
advisorDef.setSource(eleSource);
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
if (element.hasAttribute("order")) {
advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
}
parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName));
parserContext.registerComponent(compositeDef);
}
}
在configureAutoProxyCreator()
方法中,逻辑比较长,但并不复杂,大致总结一下:
- 注册事务自动代理创建器:InfrastructureAdvisorAutoProxyCreator
- 创建三个重要的BeanDefinition:
- TransactionAttributeSource(事务属性源)
- TransactionInterceptor(事务拦截器)
- TransactionAttributeSourceAdvisor(事务属性源通知器)
看到xxxAdvisor
和xxxInterceptor
结尾想必大家瞬间顿悟了吧。
此时已经封装好Advisor
增强器对象并完成了注册,剩下就是创建事务代理对象并执行增强了。
这里创建事务代理对象同Spring AOP逻辑也是一样的:
- 初始化原始
Bean
对象时调用其后置处理器 - 判断是否有符合条件的
Advisor
增强器,生成事务代理对象 - 调用事务代理对象方法时,获取拦截器
Interceptor
并递归执行
3、方法调用触发增强
在上一篇【Spring深度学习:动态代理源码解析】中了解到,调用代理类方法,实际上最后将会执行方法调用拦截器Interceptor
的invoke()
方法实现增强。
这里我们直接进入TransactionInterceptor
事务拦截器的invoke()
方法:
public Object invoke(MethodInvocation invocation) throws Throwable {
// 获取目标类的Class
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// 事务调用
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
继续进入invokeWithinTransaction()
方法,精简后的源码如下:
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// 获取事务属性源对象
TransactionAttributeSource tas = getTransactionAttributeSource();
// 获取事务属性
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 获取事务管理器对象TransactionManager
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// 创建事务,调用事务管理器对应的getTransaction()方法
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal;
try {
// 执行被增强的目标类方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 回滚事务
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 清除事务信息
cleanupTransactionInfo(txInfo);
}
// 提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
}
看到这里,声明式事务AOP实现逻辑也就非常清晰了:
没错,其本质就是基于面向切面,对目标方法前后进行拦截,然后在目标方法开始之前创建一个新事务,等方法执行完毕之后根据目标方法执行的情况选择提交或者回滚。
其中的创建事务、提交事务和回滚事务内部实际上调用的就是我们在编程式事务源码分析中认识的AbstractPlatformTransactionManager
抽象类对应的getTransaction()
、commit()
和rollback()
方法。
// 调用事务管理器创建事务
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
// 获取事务,最终依然是调用AbstractPlatformTransactionManager抽象类的getTransaction()方法
status = tm.getTransaction(txAttr);
}
}
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
// 调用事务管理器提交事务
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
// 提交事务,调用AbstractPlatformTransactionManager抽象类的commit()方法
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
// 调用事务管理器回滚事务
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 回滚事务,调用AbstractPlatformTransactionManager抽象类的rollback()方法
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
}
}
}
4、AOP事务失效
回过头来,我们再分析一下声明式事务可能导致事务失效的情形:
-
Spring没有开启事务管理器
如果Spring内部没有注入事务管理器则也无法使事务生效
例如:
<!--开启Transactional注解--> <tx:annotation-driven />
本文讲解的是使用XML配置文件开启事务支持,目前常用的SpringBoot中开启事务支持则是通过使用
@EnableTransactionManagement
注解作用于配置类上。 -
任何代理失效的方式都会导致事务失效
Spring声明式事务本质上是基于AOP,那么导致AOP失效的情况同样会导致事务失效,结合我们上一篇讲解的动态代理,如果一个类或方法被
final
修饰,则不会被创建代理。 -
异常被自己捕获处理
在源码中,我们能很清晰的看到,Spring声明式事务只有自己捕捉到了业务抛出去的异常,才能进行后续的回滚,如果我们内部手动的捕获了异常,那Spring是不知情的,也就无法触发回滚。
try { // 执行被增强的目标类方法 retVal = invocation.proceedWithInvocation(); } catch (Throwable ex) { // 回滚事务 completeTransactionAfterThrowing(txInfo, ex); throw ex; }
五、流程图
六、后记
本文从事务的基本概念开始介绍,引申到Spring实现事务控制的源码解析,过程中探究了Spring控制事务的核心,即操控数据库的自动提交,同时认识到Spring针对事务传播行为的管理策略,最后带大家分析声明式事务导致事务实现的原因,想必大家已经深入理解了Spring事务。
实际开发中更多采用的是声明式事务,得益于其的便捷性,但任何技术都有其利弊,在后续应用中应谨记AOP背后的动态代理机制导致事务失效的几种情形,有效规避带来的风险。同样,Spring声明式事务作为AOP面向切面编程的一种最佳实践,进一步体现AOP的强大之处。
如果觉得内容还不错,大家可以先点点关注,后续小豪将会更新Java领域其它系列底层源码文章哦~