
背景
上一篇我们介绍了Spring的事务案例:Spring事务管理:应用实战案例和规则
事务Transaction,它是一系列严密操作动作,要么都操作完成,要么都回滚撤销。
Spring事务管理基于底层数据库本身的事务处理机制。
数据库事务的基础,是掌握Spring事务管理的基础。
数据库事务
数据库事务具备ACID四种特性,ACID是Atomic(原子性)、Consistency(一致性)、Isolation(隔离性)和Durability(持久性)的英文缩写。
-
A:原子性(Atomicity),一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。就像你买东西要么交钱收货一起都执行,要么发不出货,就退钱。
-
C:一致性(Consistency),事务的一致性指的是在一个事务执行之前和执行之后数据库都必须处于一致性状态。如果事务成功地完成,那么系统中所有变化将正确地应用,系统处于有效状态。如果在事务中出现错误,那么系统中的所有变化将自动地回滚,系统返回到原始状态。
-
I:隔离性(Isolation),指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。打个比方,你买东西这个事情,是不影响其他人的。
-
D:持久性(Durability),指的是只要事务成功结束,它对数据库所做的更新就必须***保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。打个比方,你买东西的时候需要记录在账本上,即使老板忘记了那也有据可查。
事务的传播特性
源码
public interface TransactionDefinition {
/**
* Support a current transaction; create a new one if none exists.
*/
int PROPAGATION_REQUIRED = 0;
/**
* Support a current transaction; execute non-transactionally if none exists.
* Analogous to the EJB transaction attribute of the same name.
*/
int PROPAGATION_SUPPORTS = 1;
/**
* Support a current transaction; throw an exception if no current transaction
* exists.
*/
int PROPAGATION_MANDATORY = 2;
/**
* Create a new transaction, suspending the current transaction if one exists.
*/
int PROPAGATION_REQUIRES_NEW = 3;
/**
* Do not support a current transaction; rather always execute non-transactionally.
*/
int PROPAGATION_NOT_SUPPORTED = 4;
/**
* Do not support a current transaction; throw an exception if a current transaction
* exists
*/
int PROPAGATION_NEVER = 5;
/**
* Execute within a nested transaction if a current transaction exists,
* behave like {@link #PROPAGATION_REQUIRED} otherwise.
*/
int PROPAGATION_NESTED = 6;
}
代码解析:
Spring定义了7种传播行为:(都有2个特征,一个是对当下事务,一个是对新事务)
| 传播级别 | 当前事务是否支持 | 是否新建事务 |
| propagation_requierd | 支持当前事务 |
|
| propagation_supports | 支持当前事务 |
|
| propagation_mandatory | 支持当前事务 |
|
| propagation_required_new | 不支持 |
|
| propagation_not_supported | 不支持 |
|
| propagation_never | 不支持 |
|
| propagation_nested |
|
使用案例(声明式事务)
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
事务标签源码
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
代码分析:
定义了事务注解@Transactional相关的行为配置,具体功能如下:
-
propagation():设置事务传播行为,默认为Propagation.REQUIRED。
-
REQUIRED:支持现有事务,若无则新建。
-
SUPPORTS:支持现有事务,若无则非事务执行。
-
MANDATORY:支持现有事务,若无则抛异常。
-
REQUIRES_NEW:新建事务,若有则挂起现有事务。
-
NOT_SUPPORTED:非事务执行,若有则挂起现有事务。
-
NEVER:非事务执行,若存在事务则抛异常。
-
NESTED:若存在事务,则在嵌套事务中执行;否则与REQUIRED相同。
-
-
isolation():设置事务隔离级别,默认为Isolation.DEFAULT。主要用于新开启的事务。
-
timeout():设置事务超时时间(秒),默认使用底层事务系统的超时设置。同样适用于新开启的事务。
-
readOnly():标记事务是否为只读,默认为false。提示事务子系统进行优化。
-
rollbackFor():指定导致事务回滚的异常类型,默认为运行时异常和错误。
-
rollbackForClassName():通过异常类名指定导致事务回滚的异常类型,默认为空。
-
noRollbackFor():指定不导致事务回滚的异常类型,默认为空。
-
noRollbackForClassName():通过异常类名指定不导致事务回滚的异常类型,默认为空。
事务标签的解析源码
SpringTransactionAnnotationParser#parseTransactionAnnotation()
@Override
@Nullable
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
element, Transactional.class, false, false);
if (attributes != null) {
return parseTransactionAnnotation(attributes);
}
else {
return null;
}
}
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
Propagation propagation = attributes.getEnum("propagation");
rbta.setPropagationBehavior(propagation.value());
Isolation isolation = attributes.getEnum("isolation");
rbta.setIsolationLevel(isolation.value());
rbta.setTimeout(attributes.getNumber("timeout").intValue());
rbta.setReadOnly(attributes.getBoolean("readOnly"));
rbta.setQualifier(attributes.getString("value"));
List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
rbta.setRollbackRules(rollbackRules);
return rbta;
}
代码分析:
-
至此, 声名式事务功能的初始化工作便结束了,当判断某个 bean 适用于事务增强时,也就是适用于增强器 BeanFactoryTransactionA忧ributeSourceAdvisor, 没锚,还是这个类,所以说,在自 定义标签解析时,注入的类成为了整个事务功能的基础 。
-
BeanFactoryTransactionAttributeSourceAdvisor作为 Advisor 的实现类,自然要遵从 Advisor 的处理方式,当代理被调用时会调用这个类的增强方法,也就是此 bean 的 Advise, 又因为在 解析事务定义标签时我们把 Transactionlnterceptor 类型的 bean 注入到了 BeanFactory TransactionAttributeSourceAdvisor 中,所以,在调用事务增强器增强的代理类时会首先执行 Transactionlnterceptor进行增强,同时,也就是在 Transactionlnterceptor类中的 invoke方法中完 成了整个事务的逻辑。
事务增强器
Transactionlnterceptor 支撑着整个事务功能的架构,逻辑还是相对复杂的。
@SuppressWarnings("serial")
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
}
}
1、TransactionAspectSupport#invokeWithinTransaction:Spring事务处理
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
final TransactionManager tm = determineTransactionManager(txAttr);
// ...
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
// 声明式事务处理逻辑
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
// 编程式事务处理逻辑
final ThrowableHolder throwableHolder = new ThrowableHolder();
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) ptm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(ptm, txAttr, joinpointIdentification, status);
try {
Object retVal = invocation.proceedWithInvocation();
if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
return retVal;
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
throwableHolder.throwable = ex;
return null;
}
}
finally {
cleanupTransactionInfo(txInfo);
}
});
// Check result state: It might indicate a Throwable to rethrow.
if (throwableHolder.throwable != null) {
throw throwableHolder.throwable;
}
return result;
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
catch (TransactionSystemException ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
ex2.initApplicationException(throwableHolder.throwable);
}
throw ex2;
}
catch (Throwable ex2) {
if (throwableHolder.throwable != null) {
logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
}
throw ex2;
}
}
}
代码分析:
事务处理器,源码特地区分了 声明式事务 和 编程式事务。
从上面的函数中,我们尝试整理下事务处理的脉络,在 Spring 中支持两种事务处理的方式, 分别是声明式事务处理与编程式事务处理,两者相对于开发人员来讲差别很大,但是对于 Spring 中的实现来讲,大同小异。在 invoke 中我们也可以看到这两种方式的实现。
考虑到对事务的应用比声明式的事务处理使用起来方便,也相对流行些,我们就以此种方式进行分析。
【声明式事务处理】
声明式的事务处理主要有以下三个大步骤:
1、获取事务的属性
对于事务处理来说,最基础或者说最首要 的工作便是获取事务属性了,这是支撑整个事务功能的基石,如果没有事务属性,其他功能也无从谈起,在分析事务准备阶段时我们已 经分析 了事务属性提取的功能,大家应该有所了解。
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
代码最终会回调到上面的事务标签的解析源码的处理逻辑。
2、加载配置中配置的 TransactionManager事务处理器
3、不同的事务处理方式使用不同的逻辑(这里我们关注声明式事务),下面的源码分析,将按照下面的流程图进行
-
-
在目标方法执行前获取事务井收集事务信息 ,创建事务:createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
-
执行目标方法:invocation.proceedWithInvocation();
-
一旦出现异常,尝试异常处理 (并不是所有异常, Spring都会将其回滚,默认只对 RuntimeException 回滚。)
-
completeTransactionAfterThrowing(txInfo, ex);
-
提交事务前的事务信息清除 :cleanupTransactionInfo(txInfo);
-
提交事务:commitTransactionAfterReturning(txInfo);
-

【声明式事务处理-源码】
(1)源码分析 createTransactionIfNecessary :创建事务
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// If no name specified, apply method identification as transaction name.
if (txAttr != null && txAttr.getName() == null) {
//【1】使用 DelegatingTransactionAttr工bute 封装 txAttr
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
//【2】获取TransactionStatus
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
//【3】根据指定定的属性与 TransactionStatus 准备一个 TransactionInfo
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
代码分析:
-
【1】使用 DelegatingTransactionAttribute封装传人的 TransactionAttribute实例。
-
【2】获取TransactionStatus事务。事务处理当然是以事务为核心,那么获取事务就是最重要的事情。
-
【3】构建事务信息。根据之前几个步骤获取的信息构建 Transactionlnfo 并返回 。
(2)获取TransactionStatus事务。
源码:通过getTransaction来处理准备事务的工作。
@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {
// Use defaults if no transaction definition given.
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
//【1】获取事务
Object transaction = doGetTransaction();
if (isExistingTransaction(transaction)) {
//【2】当前线程已经存在事务
return handleExistingTransaction(def, transaction, debugEnabled);
}
// 【3】事务超时设置验证
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
}
//【4】如果当前线程不存在事务,但是 propagationBehavior 却被声明为 propagation_mandatory 则抛 异常
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
// 【5】挂起当前事务
SuspendedResourcesHolder suspendedResources = suspend(null);
try {
//【6】启动事务
return startTransaction(def, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
}
}
(2.1)startTransaction:启动事务
AbstractPlatformTransactionManager#startTransaction
private TransactionStatus startTransaction(TransactionDefinition definition, Object transaction,
boolean debugEnabled, @Nullable SuspendedResourcesHolder suspendedResources) {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
//【6.1】构造 transaction,包括:设置ConnectionHolder、隔离级别、timeout
// 如果是新连接,还要绑定当前线程
doBegin(transaction, definition);
//【6.2】新同步事务 的设置,针对于当前线程的设'fl.
prepareSynchronization(status, definition);
return status;
}
代码分析:
-
由于这里Spring仅仅提供了个接口,实际的事务管理,是交给各个数据库来处理的。
-
下面以DateSource的DataSourceTransactionManager实现为例,它实现了InnoDB对Spring事务的支持。
(2.1.1)DataSourceTransactionManager#doBegin
创建对应的事务实例,这里使用的是 DataSourceTransactionManager 中的 doGetTransaction 方法,创建基于JDBC的事务实例。
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
//【1】
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = obtainDataSource().getConnection();
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
txObject.setReadOnly(definition.isReadOnly());
if (con.getAutoCommit()) {
//【2】
txObject.setMustRestoreAutoCommit(true);
con.setAutoCommit(false);
}
prepareTransactionalConnection(con, definition);
//【3】
txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
//【4】
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
if (txObject.isNewConnectionHolder()) {
//【5】
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);
}
}
代码分析:
(可以比对JDBC连接,每次Mybatis都会创建一个SqlSession,但不会创建新的Connect对象,因为JDBC池子就是为了复用Connect对象,因此只是创建一个SqlSession时,进行属性赋值)
当然并不是每次都会获取新的连接 , 如果当前线程中的 connectionHolder 已经存在, 则没 有必要再次获取,或者 , 对于事务 同步表示设置为 true 的需要重新获取连接 。
-
【1】设置隔离级别以及只读标识。
-
事务中的只读配置是 Spring中做了一些处理呢? Spring中确实是 针对只读操作做了一些处理,但是核心的实现是设置 connection上的 readOnly属性。同样,对于隔离级别的控制也是交由 connection去控制的。
-
-
【2】更改默认的提交设置 。
-
【3】设置标志位,标识当前连接已经被事务激活 。
-
【4】设置过期时间。
-
【5】将 connectionHolder绑定到当前线程。
-
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
-
(2)invocation.proceedWithInvocation()
执行事务。
(3)completeTransactionAfterThrowing(txInfo, ex)
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
//【1】检查TransactionInfo 有设置了回滚方法
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 【2】执行回滚方法
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
throw ex2;
}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
//【3】TransactionInfo 没有设置回滚方法
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
throw ex2;
}
}
}
}
代码分析:
该方法用于在事务(txInfo)中发生异常(ex)后,完成事务的回滚或清理工作。主要处理异常情况下的事务一致性问题。
该方法用于在发生异常后完成事务处理。主要功能包括:
-
【1】检查TransactionInfo 是否有设置了回滚方法
-
【2】执行回滚方法,则尝试回滚并捕获可能的异常
-
【3】如果不需要回滚,则尝试提交事务,并捕获可能的异常
-
(4)、cleanupTransactionInfo(txInfo)
protected void cleanupTransactionInfo(@Nullable TransactionInfo txInfo) {
if (txInfo != null) {
txInfo.restoreThreadLocalStatus();
}
}
代码分析:
-
cleanupTransactionInfo用于清理事务信息,如果txInfo不为空,则调用其restoreThreadLocalStatus方法恢复线程本地状态
-
更新transactionInfoHolder为老的事务信息
(5)、commitTransactionAfterReturning(txInfo)
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
//【1】
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
代码分析:
该方法在事务处理结束后提交事务,具体功能如下:
-
检查传入的txInfo对象是否非空且其内部的TransactionStatus对象也非空
-
使用txInfo中的事务管理器提交事务。
总结
事务管理是其Spring框架的核心功能之一,它提供了声明式事务管理和编程式事务管理两种方式;Spring并不会直接管理事务,而是提供了事务管理器,通过AOP实现,主要步骤包括配置事务管理器、启用事务管理、定义事务边界、创建事务代理、事务拦截器处理、事务同步管理和事务资源管理。
底层调用的是getConnection().setAutocommit(false);或connection.commit(),通过这种方式,Spring能够透明地为应用程序提供事务支持,简化了事务管理的复杂性。
但基于JDBC的Spring事务,只能实现单节点的事务管理,对分布式事务并不支持。
在分布式系统中,事务管理是一个复杂的问题,因为涉及到多个节点的数据一致性和完整性,选择不同的方案要根据具体的业务需求、系统架构和性能要求来分析,我们后续再讨论一二。
其他文章
多线程反思(中):对ThreadPoolExecutors的思考
SpringCloud源码:客户端分析(一)- SpringBootApplication注解类加载流程

168万+

被折叠的 条评论
为什么被折叠?



