老生常谈系列之Aop--Aop的经典应用之Spring的事务实现分析(三)
老生常谈系列之Aop--Aop的经典应用之Spring的事务实现分析(三)
前言
上一篇文章老生常谈系列之Aop--Aop的经典应用之Spring的事务实现分析(二)从三个问题导入,分析了Spring是如何开启事务的、Spring是如何为需要事务支持的类生成代理的以及Spring是怎么实现成功提交异常回滚的执行框架的。但是在这个执行框架里,事务是怎么进行的并没有详细说明。这里面会有非常多的问题,例如如何根据配置的事务属性去完成相应的事务行为、什么时候创建一个新事务什么时候在当前事务执行又或者是嵌套事务执行、事务成功或者失败后事务的状态信息怎么流转的等等。细节很多,这篇文章肯定覆盖不了全部,但是这篇文章会详细从源码的角度去分析部分实现细节。
这里要声明一下在Spring里,事务的最终实现还是依赖底层数据库去实现的,这里只是利用了数据库的实现,数据库事务的实现原理,以Mysql为例细节可以见Mysql事务实现原理。所以Spring并没有真正意义上去实现了事务的各种特性,只是利用了底层的实现。那下面我们开始分析在Spring的事务执行框架里,事务的各种细节操作吧_。
源码分析
事务执行总流程
首先回到上文老生常谈系列之Aop--Aop的经典应用之Spring的事务实现分析(二)事务执行框架的代码位置,代码位于TransactionAspectSupport#invokeWithinTransaction(Method method, @Nullable Class<?> targetClass, final InvocationCallback invocation)
方法中,这个方法的代码如下,并且附上了注释,那么本文就来详细分析一把这个方法里面的细节。
/**
* General delegate for around-advice-based subclasses, delegating to several other template
* methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager}
* as well as regular {@link PlatformTransactionManager} implementations.
* 对于声明式事务的处理与编程式事务的处理,
* 第一点区别在于事务属性上,因为编程式的事务处理是不需要有事务属性的,
* 第二点区别就是在 TransactionManager上, CallbackPreferringPlatformTransactionManager 实现 PlatformTransactionManager 接口,
* 暴露出一个方法用于执行事务务处理中的回调。
* 所以 ,这两种方式都可以用作事务处理方式的判断
* @param method the Method being invoked
* @param targetClass the target class that we're invoking the method on
* @param invocation the callback to use for proceeding with the target invocation
* @return the return value of the method, if any
* @throws Throwable propagated from the target invocation
*/
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
if (this.reactiveAdapterRegistry != null) {
if (KotlinDetector.isKotlinType(method.getDeclaringClass()) && KotlinDelegate.isSuspend(method)) {
throw new TransactionUsageException("Unsupported annotated transaction on suspending function detected: "
+ method + ". Use TransactionalOperator.transactional extensions instead.");
}
ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(method.getReturnType());
if (adapter != null) {
return new ReactiveTransactionSupport(adapter).invokeWithinTransaction(method, targetClass, invocation);
}
}
// If the transaction attribute is null, the method is non-transactional.
TransactionAttributeSource tas = getTransactionAttributeSource();
// 获取对应事务属性
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 获取 beanFactory 中的 transactionManager
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
// 构造唯一标识 -> 类.方法 io.codegitz.UserServiceImpl.save
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
// 声明式事务处理
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
// 创建TransactionInfo
TransactionInfo txInfo = createTransactionIfNecessary(tm, 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.
// 这是一个 around advice:调用链中的下一个拦截器。这通常会导致调用目标对象。
// 执行增强方法
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) tm).execute(txAttr, status -> {
TransactionInfo txInfo = prepareTransactionInfo(tm, 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、获取事务的属性。 对于事务处理来说,最基础或者说最首要的工作便是获取事务属性了,这是支撑整个事务 功能的基石,如果没有事务属性,其他功能也无从谈起,在分析事务准备阶段时我们已经分析 了事务属性提取的功能,大家应该有所了解。
2、加载配置中配置的 TransactionManager
。
3、不同的事务处理方式使用不同的逻辑。 对于声明式事务的处理与编程式事务的处理,第一点区别在于事务属性上, 因为编程式的事务处理是不需要有事务属性的,第二点区别就是在 TransactionManager
上, CallbackPreferringPlatformTransactionManager
实现 PlatfonnTransactionManager
接口, 暴露出一个方法用于执行事务处理中的回调。 所以 ,这两种方式都可以用作事务处理方式的判断。这里会以声明式事务为例,这是比较常用比较流行的用法。
4、在目标方法执行前获取事务并收集事务信息。 事务信息与事务属性并不相同,也就是 Transactionlnfo
与 TransactionAttribute
并不相同, Transactionlnfo
中包含 TransactionAttribute
信息,但是,除了 TransactionAttribute
外还有其他事务 信息 ,例如 PlatformTransactionManager
以及 TransactionStatus
相关信息。
5、执行目标方法。
6、一旦出现异常,尝试异常处理。 并不是所有异常, Spring 都会将其回滚,默认只对 RuntimeException
回滚。
7、提交事务前的事务信息清除。
8、提交事务 。
上面的步骤分析旨在让大家对事务功能与步骤有个大致的了解,具体的功能还需要详细地分析。
获取TransactionAttributeSource
进入TransactionAspectSupport#invokeWithinTransaction()
方法中,可以看到先判断是否是reactive
编程,设置相关的属性,然后获取了TransactionAttributeSource
,这里获取的机会我们配置的AnnotationTransactionAttributeSource
,所以这里的tas
肯定不为空,然后下面利用tas.getTransactionAttribute(method, targetClass)
方法获取事务属性TransactionAttribute
。
跟进tas.getTransactionAttribute(method, targetClass)
方法。
这里会进入AbstractFallbackTransactionAttributeSource#getTransactionAttribute()
方法。
public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
if (method.getDeclaringClass() == Object.class) {
return null;
}
// First, see if we have a cached value.
// 首先去缓存里面查
Object cacheKey = getCacheKey(method, targetClass);
TransactionAttribute cached = this.attributeCache.get(cacheKey);
if (cached != null) {
// Value will either be canonical value indicating there is no transaction attribute,
// or an actual transaction attribute.
if (cached == NULL_TRANSACTION_ATTRIBUTE) {
return null;
}
else {
return cached;
}
}
else {
// We need to work it out.
// 获取事务的属性
TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
// Put it in the cache.
if (txAttr == null) {
this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
}
else {
// 生成事务的名称
String methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
if (txAttr instanceof DefaultTransactionAttribute) {
((DefaultTransactionAttribute) txAttr).setDescriptor(methodIdentification);
}
if (logger.isTraceEnabled()) {
logger.trace("Adding transactional method '" + methodIdentification + "' with attribute: " + txAttr);
}
this.attributeCache.put(cacheKey, txAttr);
}
return txAttr;
}
}
可以看到这里首先去缓存获取,获取不到再去computeTransactionAttribute(method, targetClass)
获取,跟进方法。
/**
* Same signature as {@link #getTransactionAttribute}, but doesn't cache the result.
* {@link #getTransactionAttribute} is effectively a caching decorator for this method.
* <p>As of 4.1.8, this method can be overridden.
* @since 4.1.8
* @see #getTransactionAttribute
*/
@Nullable
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// Don't allow no-public methods as required.
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
// method 代表接口中的方法, specificMethod 代表实现类中的方法
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
// First try is the method in the target class.
// 查看方法中是有存在事务声明
TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
if (txAttr != null) {
return txAttr;
}
// Second try is the transaction attribute on the target class.
// /查看方法所在类中是否存在事务声明
txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
if (specificMethod != method) {
// Fallback is to look at the original method.
// 如果存在接口,则到接口中去寻找
txAttr = findTransactionAttribute(method);
if (txAttr != null) {
return txAttr;
}
// Last fallback is the class of the original method.
// 到接口中的类中寻找
txAttr = findTransactionAttribute(method.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
}
return null;
}
对于事务属性的获取规则相信大家都已经很清楚,如果方法中存在事务属性,则使用方法上的属性,否则使用方法所在的类上的属性,如果方法所在类的属性上还是没有搜寻到对应的事务属性,那么再搜寻接口中的方法,再没有的话,最后尝试搜寻接口的类上面的声明。 对于函数 computeTransactionAttribute()
中的逻辑与我们所认识的规则并无差别,但是上面函数中并没有真正的去做搜寻事务属性的逻辑,而是搭建了个执行框架,将搜寻事务属性的任务委托给了findTransactionAttribute()
方法去执行。
可以看到这里先AopUtils.getMostSpecificMethod(method, targetClass)
获取最具体的方法,因为传入的方法可能是在接口上的方法,注解属性可能在实现类上,所以需要先获取最具体的方法specificMethod
。然后调用findTransactionAttribute(specificMethod)
方法获取事务属性txAttr
。
protected TransactionAttribute findTransactionAttribute(Method method) {
return determineTransactionAttribute(method);
}
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
for (TransactionAnnotationParser parser : this.annotationParsers) {
TransactionAttribute attr = parser.parseTransactionAnnotation(element);
if (attr != null) {
return attr;
}
}
return null;
}
this.annotationParsers
是在当前类 AnnotationTransactionAttributeSource
初始化的时候初始化的 , 其中的值被加入了SpringTransactionAnnotationParser
, 也就是当进行属性获取的时候其实是使用 SpringTransactionAnnotationParser
类的 parseTransactionAnnotation()
方法进行解析的。
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
element, Transactional.class, false, false);
if (attributes != null) {
return parseTransactionAnnotation(attributes);
}
else {
return null;
}
}
至此 ,我们终于看到了想看到 的获取注解标记的代码。 首先会判断当前的类是否含有 Transactional
注解,这是事务属性的基础,当然如果有的话会继续调用parseTransactionAnnotation()
方法解析详细的属性。
/**
* 我们的现在的任务是找出某个增强器是否适合于对应的类,
* 而是否匹配的关键则在于是否从指定的类或类中的方法中找到对应的事务属性,
* 现在,我们以 UserServiceImpl 为例,已经在它的接口 UserService 中找到了事务属性,
* 所以,它是与事务增强器匹配的,也就是它会被事务功能修饰。
* 至此,事务功能的初始化工作便结束了,
* 当判断某个 bean 适用于事务增强时,也就是适用于增强器 {@link BeanFactoryTransactionAttributeSourceAdvisor},
* 没锚,还是这个类,所以说,在自定义标签解析时,注入的类成为了整个事务功能的基础。
* {@link BeanFactoryTransactionAttributeSourceAdvisor} 作为 Advisor 的实现类,自然要遵从 Advisor 的处理方式,
* 当代理被调用时会调用这个类的增强方法,也就是此 bean 的 Advise,
* 又因为在解析事务定义标签时我们把 {@link TransactionInterceptor} 类型的 bean 注入到了 {@link BeanFactoryTransactionAttributeSourceAdvisor} 中,
* 所以,在调用事务增强器增强的代理类时会首先执行 {@link TransactionInterceptor} 进行增强,
* 同时,也就是在 {@link TransactionInterceptor#invoke} 方法中完成了整个事务的逻辑。
* @param attributes
* @return
*/
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
// 解析propagation
Propagation propagation = attributes.getEnum("propagation");
rbta.setPropagationBehavior(propagation.value());
// 解析isolation
Isolation isolation = attributes.getEnum("isolation");
rbta.setIsolationLevel(isolation.value());
// 解析timeout
rbta.setTimeout(attributes.getNumber("timeout").intValue());
// 解析readOnly
rbta.setReadOnly(attributes.getBoolean("readOnly"));
// 解析value
rbta.setQualifier(attributes.getString("value"));
List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
// 解析rollbackFor
for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
// 解析rollbackForClassName
for (String rbRule : attributes.getStringArray("rollbackForClassName")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
// 解析noRollbackFor
for (Class<?> rbRule : attributes.getClassArray("noRollbackFor")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
// 解析noRollbackForClassName
for (String rbRule : attributes.getStringArray("noRollbackForClassName")) {
rollbackRules.add(new NoRollbackRuleAttribute(rbRule));
}
rbta.setRollbackRules(rollbackRules);
return rbta;
}
上面方法中实现了对对应类或者方法的事务属性解析,你会在这个类中看到任何你常用或者不常用的属性提取。
至此,事务功能的初始化工作便结束了,当判断某个 bean
适用于事务增强时,也就是适用于增强器 BeanFactoryTransactionAttributeSourceAdvisor
, 没错,还是这个类,所以说,在自定义标签解析时,注入的类成为了整个事务功能的基础。BeanFactoryTransactionAttributeSourceAdvisor
作为 Advisor
的实现类,自然要遵从 Advisor
的处理方式,当代理被调用时会调用这个类的增强方法,也就是此bean
的 Advise
, 又因为在解析事务定义标签时我们把 Transactionlnterceptor
类型的 bean
注入到了 BeanFactory TransactionAttributeSourceAdvisor
中,所以,在调用事务增强器增强的代理类时会首先执行 Transactionlnterceptor
进行增强,同时,也就是在 Transactionlnterceptor
类中的 invoke()
方法中完 成了整个事务的逻辑。
获取PlatformTransactionManager
上面已经获取了事务属性,那么接下来就要获取指定的PlatformTransactionManager
跟进determineTransactionManager(txAttr)
方法。这个方法比较简单,就是获取根据各种条件获取一个PlatformTransactionManager
。
/**
* Determine the specific transaction manager to use for the given transaction.
*/
@Nullable
protected PlatformTransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
// Do not attempt to lookup tx manager if no tx attributes are set
if (txAttr == null || this.beanFactory == null) {
return asPlatformTransactionManager(getTransactionManager());
}
// 是否有指定qualifier,如果有则去查找qualifier为name的bean
String qualifier = txAttr.getQualifier();
if (StringUtils.hasText(qualifier)) {
return determineQualifiedTransactionManager(this.beanFactory, qualifier);
}
// 是否有指定transactionManagerBeanName,如果有则去查找transactionManagerBeanName为name的bean
else if (StringUtils.hasText(this.transactionManagerBeanName)) {
return determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
}
else {
// 没有则获取默认的,默认的是按照类型获取PlatformTransactionManager.class的所有bean
PlatformTransactionManager defaultTransactionManager = asPlatformTransactionManager(getTransactionManager());
if (defaultTransactionManager == null) {
defaultTransactionManager = asPlatformTransactionManager(
this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY));
if (defaultTransactionManager == null) {
defaultTransactionManager = this.beanFactory.getBean(PlatformTransactionManager.class);
this.transactionManagerCache.putIfAbsent(
DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
}
}
return defaultTransactionManager;
}
}
创建声明式事务
上面已经获取了事务的属性TransactionAttribute
和事务管理器PlatformTransactionManager
。
protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
// If no name specified, apply method identification as transaction name.
// 如果没有名称指定则使用方法唯一标识,并使用 DelegatingTransactionAttribute 封装 txAttr
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
// 获取TransactionStatus
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
// 准备事务信息,根据指定的属性与TransactionStatus准备一个TransactionInfo
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
对于 createTransactionIfNecessary()
函数主要做了这样几件事情。
- 使用
DelegatingTransactionAttribute
封装传入的TransactionAttribute
实例。 对于传入的TransactionAttribute
类型的参数txAttr
,当前的实际类型是RuleBasedTransactionAttribute
,是由获取事务属性时生成,主要用于数据承载,而这里之所以使用DelegatingTransactionAttribute
进行封装 , 当然是提供了更多的功能。 - 获取事务。 事务处理当然是以事务为核心,那么获取事务就是最重要的事情。
- 构建事务信息。 根据之前几个步骤获取的信息构建
TransactionInfo
并返回。 我们分别对以上步骤进行详细的解析。
封装TransactionAttribute
这一步没啥好说的就是封装了一个DelegatingTransactionAttribute
,这个东西为啥要委托一下呢?看起来相当怪异,直接用不行?摘取类上的注释如下:
{@link TransactionAttribute} implementation that delegates all calls to a given target
{@link TransactionAttribute} instance. Abstract because it is meant to be subclassed,
with subclasses overriding specific methods that are not supposed to simply delegate
to the target instance.
这个类主要作用是把调用的方法转发到目标类上,同时该类是抽象的,需要实现没实现的方法,这是是实现了getName()
方法,所以我猜测这个委托类最大的意义就是给不同的事务方法添加个名字属性joinpointIdentification
。
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
获取事务
接下来就是获取事务的操作了,这里会比较复杂一点,因为事务的属性很多,特别是涉及嵌套事务就会相对复杂。
我们直接跟进getTransaction()
方法,调用了父类AbstractPlatformTransactionManager
的实现。这个方法还是比较长的,但是逻辑是清晰的,跟着注释看一遍,大概就能理解逻辑。实话说,看一遍肯定是没什么卵用的,如果真的要学习,起码要看五遍。我为了水文,看了已经不下十遍。
/**
* This implementation handles propagation behavior. Delegates to
* {@code doGetTransaction}, {@code isExistingTransaction}
* and {@code doBegin}.
* @see #doGetTransaction
* @see #isExistingTransaction
* @see #doBegin
*
* 在 Spring复杂的功能实现,并不是一次完成的,而会通过入口函数进行一个框架的搭建,
* 初步构建完整的逻辑,而将实现细节分摊给不同的函数。那么,让我们看看事
* 务的准备工作都包括哪些
*/
@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.获取事务 DataSourceTransactionManager#doGetTransaction()
Object transaction = doGetTransaction();
boolean debugEnabled = logger.isDebugEnabled();
// 2.如果当先线程存在事务,则转向嵌套事务的处理
// 判断是否存在事务,判断依据为当前线程记录的连接数不为空且连接中(connectionHolder)中的transactionActive属性不为空
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
// 当前线程已经存在事务
return handleExistingTransaction(def, transaction, debugEnabled);
}
// 3.事务超时设置验证
// Check definition settings for new transaction.
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
}
// 4.事务 propagationBehavior 属性的设置验证
// No existing transaction found -> check propagation behavior to find out how to proceed.
// 如果当前线程不存在事务, 但是 propagation behavior 却被声明为 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) {
// PROPAGATION _REQUIRED 、 PROPAGATION REQUIRES_NEW、 PROPAGATION_NESTED 都需要新建事务
// 空挂起
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
}
try {
// 5. 构建DefaultTransactionStatus
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
def, transaction, true, newSynchronization, debugEnabled, suspendedResources);
// 6.构建transaction ,包括设 connectionHolder 隔离级别、 timeout ,如果是新连接,则绑定到当前线程
doBegin(transaction, def);
// 新同步事务的设置,针对于当前线程的设置
prepareSynchronization(status, def);
return status;
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + def);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
}
}
上面注释已经很清晰,我们每一点都跟进去看一下
1、首先是doGetTransaction()
获取事务,由于我们是基于JDBC的示例,所以这里的实现类为DataSourceTransactionManager
。如果当前线程中存在关于 dataSource
的连接,那么直接使用。 这里有一个对保存点的设置,是否开启允许保存点取决于是否设置了允许嵌入式事务。
/**
* DataSourceTransactionManager 中的 doGetTransaction 方法,创建基于 JDBC 的事务实例。
* 如果当前线程中存在关于 dataSource 的连接,那么直接使用。
* 这里有一个对保存点的设置,是否开启允许保存点取决于是否设置了允许嵌入式事务
* @return
*/
@Override
protected Object doGetTransaction() {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
// 如果当前线程已经记录数据库连接,则使用原有连接
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
// false 表示非新创建连接
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
2、是否存在嵌套事务,判断依据为当前线程记录的连接数不为空且连接中(connectionHolder)中的transactionActive属性不为空。如果存在嵌套事务,则转向嵌套事务处理。判断是否存在嵌套事务的方法为isExistingTransaction()
。
@Override
protected boolean isExistingTransaction(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}
如果存在嵌套事务,则通过handleExistingTransaction()
方法转向嵌套事务处理,这里先不分析嵌套事务的处理,先分析默认的事务流程,嵌套事务的逻辑其实也类似,只不过是套了个娃。
3、事务超时设置验证
这里判断事务的超时时间不能小于默认的最小时间,没啥好说的。
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
}
4、事务 propagationBehavior 属性的设置验证
判断如果当前没有事务,但是事务声明为非空,直接抛出异常。然后PROPAGATION_REQUIRED
、PROPAGATION_REQUIRES_NEW
以及PROPAGATION_NESTED
都需要新建事务。最后Create "empty" transaction: no actual transaction, but potentially synchronization.
,不知道是什么骚操作。
5、构建DefaultTransactionStatus
这一步也比较简单,就是把参数传入,封装成DefaultTransactionStatus
对象返回。
6、构建transaction
这一步包括设 connectionHolder 隔离级别、 timeout ,如果是新连接,则绑定到当前线程。
跟进doBegin()
方法,依然是DataSourceTransactionManager
的实现。
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
// 设置隔离级别
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
// 更改自动提交设置, 由 Spring 控制提交
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);
}
// Bind the connection holder to the thread.
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);
}
}
可以说事务是从这个函数开始的 ,因为在这个函数中已经开始尝试了对数据库连接的获取,当然,在获取数据库连接的同时, 一些必要的设置也是需要同步设置的。
尝试获取连接。 当然并不是每次都会获取新的连接, 如果当前线程中的 connectionHolder 已经存在, 则没 有必要再次获取,或者, 对于事务同步表示设置为 true 的需要重新获取连接。
设置隔离级别以及只读标识。 你是否有过这样的错觉? 事务中的只读配置是 Spring 中做了一些处理呢? Spring 中确实是 针对只读操作做了一些处理,但是核心的实现是设置 connection 上的 readOnly 属性。 同样,对 于隔离级别的控制也是交由 connection 去控制的。
更改默认的提交设置。 如果事务属性是自动提交, 那么需要改变这种设置,而将提交操作委托给 Spring 来处理。
设置标志位,标识当前连接已经被事务激活。
设置过期时间。
将 connectionHolder 绑定到当前线程。
设置隔离级别的DataSourceUtils.prepareConnectionForTransaction()
函数用于负责对底层数据库连接的设置, 当然,只是包含只读标识和隔离级别的设置。 由于强大的日志及异常处理,显得函数代码量比较大, 但是单从业务角度去看,关键代码其实是不多。
跟进DataSourceUtils.prepareConnectionForTransaction()
代码,主要就是属性设置,没啥可说的。
public static Integer prepareConnectionForTransaction(Connection con, @Nullable TransactionDefinition definition)
throws SQLException {
Assert.notNull(con, "No Connection specified");
// Set read-only flag.
// 设置数据连接的只读标识
if (definition != null && definition.isReadOnly()) {
try {
if (logger.isDebugEnabled()) {
logger.debug("Setting JDBC Connection [" + con + "] read-only");
}
con.setReadOnly(true);
}
catch (SQLException | RuntimeException ex) {
Throwable exToCheck = ex;
while (exToCheck != null) {
if (exToCheck.getClass().getSimpleName().contains("Timeout")) {
// Assume it's a connection timeout that would otherwise get lost: e.g. from JDBC 4.0
throw ex;
}
exToCheck = exToCheck.getCause();
}
// "read-only not supported" SQLException -> ignore, it's just a hint anyway
logger.debug("Could not set JDBC Connection read-only", ex);
}
}
// Apply specific isolation level, if any.
// 设置数据库连接的隔离级别
Integer previousIsolationLevel = null;
if (definition != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
if (logger.isDebugEnabled()) {
logger.debug("Changing isolation level of JDBC Connection [" + con + "] to " +
definition.getIsolationLevel());
}
int currentIsolation = con.getTransactionIsolation();
if (currentIsolation != definition.getIsolationLevel()) {
previousIsolationLevel = currentIsolation;
con.setTransactionIsolation(definition.getIsolationLevel());
}
}
return previousIsolationLevel;
}
7、将事务信息记录在当前线程中
获取事务成功后,会把事务信息保存到当前的线程中,prepareSynchronization()
方法会完成这些操作。主要就是把各种属性保存到当前的ThreadLocal中。
/**
* Initialize transaction synchronization as appropriate.
* 将事务信息记录在当前线程中
*/
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.setActualTransactionActive(status.hasTransaction());
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(
definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT ?
definition.getIsolationLevel() : null);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
TransactionSynchronizationManager.setCurrentTransactionName(definition.getName());
TransactionSynchronizationManager.initSynchronization();
}
}
到这里一般的事务已经创建完成,但是还记得我们上面说到的嵌套事务吗?这种复杂的事务是怎么处理的呢,下面就来看一下嵌套事务的处理。
/**
* Create a TransactionStatus for an existing transaction.
* 处理已经存在的事务
* 之前分析了普通事务建立的过程,
* 但是 Spring 中支持多种事务的传播规则,
* 比如PROPAGATION NESTED、 PROPAGATION_REQUIRES_NEW 等,
* 这些都是在已经存在事务的基础上进行进一步的处理,那么,对于已经存在的事务,准备操作是如何进行的呢?
*
* <p>PROPAGATION_阻QUIRES NEW 表示当前方法必须在它自己的事务里运行,一个新的事务将被启动,
* 而如果有一个事务正在运行的话,则在这个方法运行期间被挂起。
* 而 Spring 中对于此种传播方式的处理与新事务建立最大的不同点在于使用 suspend 方法将原事务挂起。
* 将信息挂起的目的当然是为了在当前事务执行完毕后在将原事务还原。
* <p>PROPAGATION NESTED 表示如果当前正有一个事务在运行中,则该方法应该运行在一个嵌套的事务中,
* 被嵌套的事务可以独立于封装事务进行提交或者回滚,如果封装事务不存在,行为就像 PROPAGATION_REQUIRES_NEW。
* 对于嵌入式事务的处理, Spring 中主要考虑了两种方式的处理.
* 1.Spring 中允许嵌入事务的时候,则首选设直保存点的方式作为异常处理的回滚。
* 2.对于其他方式,比如 JTA 无法使用保存点的方式,那么处理方式与 PROPAGATION_REQUIRES_NEW 相同 ,
* 而一旦出现异常,则由 Spring 的事务异常处理机制去完成后续操作。
*/
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
// 新事务的建立
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;
}
}
// 嵌入式事务的处理
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'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
if (useSavepointForNestedTransaction()) {
// Create savepoint within existing Spring-managed transaction,
// through the SavepointManager API implemented by TransactionStatus.
// Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
// 如果没有可以使用保存点的方式控制事务回滚,那么在嵌入式事务的建立初始建立保存点
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
return status;
}
else {
// Nested transaction through nested begin and commit/rollback calls.
// Usually only for JTA: Spring synchronization might get activated here
// in case of a pre-existing JTA transaction.
// 有些情况是不能使用保存点操作,比如 JTA,那么建立新事务
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, null);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
if (isValidateExistingTransaction()) {
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
Constants isoConstants = DefaultTransactionDefinition.constants;
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] specifies isolation level which is incompatible with existing transaction: " +
(currentIsolationLevel != null ?
isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
"(unknown)"));
}
}
if (!definition.isReadOnly()) {
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
throw new IllegalTransactionStateException("Participating transaction with definition [" +
definition + "] is not marked as read-only but existing transaction is");
}
}
}
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
这里分了几种情况去判断,嵌套事务跟上面分析的事务最大的区别是嵌套的事务会根据不同情况有一个事务挂起的suspend()
操作,这里的挂起也很简单,只是把设置都设为默认值,原有的属性会作为一个SuspendedResourcesHolder
返回,以便于后续操作对事务的恢复 。
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();
try {
Object suspendedResources = null;
if (transaction != null) {
suspendedResources = doSuspend(transaction);
}
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) {
// doSuspend failed - original transaction is still active...
doResumeSynchronization(suspendedSynchronizations);
throw ex;
}
}
else if (transaction != null) {
// Transaction active but no synchronization active.
Object suspendedResources = doSuspend(transaction);
return new SuspendedResourcesHolder(suspendedResources);
}
else {
// Neither transaction nor synchronization active.
return null;
}
}
准备事务信息
当已经建立事务连接并完成了事务信息的提取后,我们需要将所有的事务信息统一记录在 Transactionlnfo
类型的实例中,这个实例包含了目标方法开始前的所有状态信息,一旦事务执行失败, Spring 会通过 Transactionlnfo
类型的实例中的信息来进行回滚等后续工作。
protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,
@Nullable TransactionAttribute txAttr, String joinpointIdentification,
@Nullable TransactionStatus status) {
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
if (txAttr != null) {
// We need a transaction for this method...
if (logger.isTraceEnabled()) {
logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
// The transaction manager will flag an error if an incompatible tx already exists.
// 记录事务状态
txInfo.newTransactionStatus(status);
}
else {
// The TransactionInfo.hasTransaction() method will return false. We created it only
// to preserve the integrity of the ThreadLocal stack maintained in this class.
if (logger.isTraceEnabled()) {
logger.trace("No need to create transaction for [" + joinpointIdentification +
"]: This method is not transactional.");
}
}
// We always bind the TransactionInfo to the thread, even if we didn't create
// a new transaction here. This guarantees that the TransactionInfo stack
// will be managed correctly even if no transaction was created by this aspect.
txInfo.bindToThread();
return txInfo;
}
到这里,事务的准备工作已经全部完成了。我们改复用的事务复用了,该新建的新建了,下面就到了业务方法的执行invocation.proceedWithInvocation()
,这就是我们的业务逻辑,执行之后会正常提交,异常回滚,那么接下来就分析一下是怎么做提交和回滚的。
异常回滚
之前已经完成了目标方法运行前的事务准备工作,而这些准备工作最大的目的元非是对于程序没有按照我们期待的那样进行,也就是出现特定的错误,那么,当出现错误的时候, Spring 是怎么对数据进行恢复的呢?
/**
* Handle a throwable, completing the transaction.
* We may commit or roll back, depending on the configuration.
* 在对目标方法的执行过程中,一旦出现 Throwable 就会被引导至此方法处理,
* 但是并不代表所有的 Throwable 都会被回滚处理,比如我们常用的 Exception, 默认是不会被处理的。
* 默认情况下,即使出现异常,数据也会被正常提交,
* 而这个关键的地方就是在 txInfo.transactionAttribute.rollbackOn(ex)这个函数 {@link DefaultTransactionAttribute#rollbackOn(Throwable)}。
*
* @param txInfo information about the current transaction
* @param ex throwable encountered
*/
protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
// 当抛出异常时首先判断当前是否存在事务,这是基础依据
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
"] after exception: " + ex);
}
// 这里判断是否回滚默认的依据是抛出的异常是存是 RuntimeException 或者是 Error 的类型
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 根据 TransactionStatus 信息进行回滚处理
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
// 如果不满足问滚条件即使抛出异常也同样会提交
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException | Error ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
}
}
}
在对目标方法的执行过程中,一旦出现 Throwable
就会被引导至此方法处理,但是并不代 表所有的 Throwable
都会被回滚处理,比如我们常用的 Exception
, 默认是不会被处理的。默 认情况下,即使出现异常,数据也会被正常提交,而这个关键的地方就是在 txInfo.transactionAttribute.rollbackOn(ex)
这个函数。
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
看到了吗?默认情况下 Spring 中的事务异常处理机制只对 RuntimeException
和 Error
两种情况感兴趣,当然你可以通过扩展来改变,不过,我们最常用的还是使用事务提供的属性设置,利用注解方式的使用,例如:
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
一但满足异常回滚的条件,则会走到rollback()
方法里。
同样,对于在 Spring 中的复杂的逻辑处理过程,在入口函数一般都会给出个整体的处理脉络,而把实现细节委托给其他两数去执行。
我们尝试总结下 Spring 中对于回滚处理的大致脉络如下。
- 首先是自定义触发器的调用 ,包括在回滚前、完成回滚后的调用,
当然完成回滚包括正常回滚与回滚过程中出现异常,向定义的触发器会根据这些信息作进一步处理,
而对于触发器的注册,常见是在回调过程中通过 TransactionSynchronizationManager 类中的静态方法{@link TransactionSynchronizationManager#registerSynchronization(TransactionSynchronization)}直接注册 - 除了触发监昕函数外,就是真正的回滚逻辑处理了。
2.1 当之前已经保存的事务信息中有保存点信息的时候,使用保存点信息进行回滚。
常用于嵌入式事务,对于嵌入式的事务的处理,内嵌的事务异常并不会引起外部事务的回滚。
根据保存点回滚的实现方式其实是根据底层的数据库连接进行的
这里使用的是 JDBC 的方式进行数据库连接,那么 getSavepointManager()函数返回的是 JdbcTransactionObjectSupport ,
也就是说上面函数会调用 JdbcTransactionObjectSupport 中的 rollbackToSavepoint 方法
2.2 当之前已经保存的事务信息中的事务为新事务,那么直接回滚。
常用于单独事务的处理对于没有保存点的回滚,Spring 同样是使用底层数据库连接提供的 API 来操作的。
由于我们使用的是 DataSourceTransactionManager,那么 doRollback 函数会使用此类中的实现
2.3 当前事务信息中表明是存在事务的,又不属于以上两种情况,多数用于 JTA ,只做回滚标识,等到提交的时候统一不提交
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);
}
/**
* Process an actual rollback.
* The completed flag has already been checked.
* @param status object representing the transaction
* @throws TransactionException in case of rollback failure
*/
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;
try {
// 激活所有 TransactionSynchronization 中对应的方法
triggerBeforeCompletion(status);
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
// 如果有保存点,也就是当前事务为单独的线程则会退到保存点
status.rollbackToHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
// 如果当前事务为独立的新事务 ,则直接回退
doRollback(status);
}
else {
// Participating in larger transaction
if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
// 如果当前事务不是独立的事务,那么只能标记状态,等到事务链执行完后统一回滚
doSetRollbackOnly(status);
}
else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
else {
logger.debug("Should roll back transaction but cannot - no transaction available");
}
// Unexpected rollback only matters here if we're asked to fail early
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
}
catch (RuntimeException | Error ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
// 激活所有 transactionSynchronization 中对应的方法
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
// Raise UnexpectedRollbackException if we had a global rollback-only marker
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
}
finally {
// 清空记录的资源并将挂起的资源、恢复
cleanupAfterCompletion(status);
}
}
调用DataSourceTransactionManager#doRollback()
直接回滚,这里con
使用的JDBC的连接。
@Override
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
对于回滚逻辑执行结束后,无论回滚是有成功,都必须要做的事情就是事务结束后的收尾工作。 进入到cleanupAfterCompletion()
方法。
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
// 设置完成状态
status.setCompleted();
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.clear();
}
if (status.isNewTransaction()) {
doCleanupAfterCompletion(status.getTransaction());
}
if (status.getSuspendedResources() != null) {
if (status.isDebug()) {
logger.debug("Resuming suspended transaction after completion of inner transaction");
}
Object transaction = (status.hasTransaction() ? status.getTransaction() : null);
// 结束之前事务的挂起状态
resume(transaction, (SuspendedResourcesHolder) status.getSuspendedResources());
}
}
如果是新事物的话调用doCleanupAfterCompletion()
直接清理,如果是嵌套事务,需要调用resume()
把外层事务唤醒。
/**
* 1.设置状态是对事务信息作完成标识以避免重复调用 。
* 2.如果当前事务是新的同步状态,需要将绑定到当前线程的事务信息清除。
* 3.如果是新事务需要做些清除资源的工作
* @param transaction transaction object returned by {@code doGetTransaction}
*/
@Override
protected void doCleanupAfterCompletion(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// Remove the connection holder from the thread, if exposed.
if (txObject.isNewConnectionHolder()) {
// 将数据库连接从当前线程中解除绑定
TransactionSynchronizationManager.unbindResource(obtainDataSource());
}
// Reset connection.
// 释放连接
Connection con = txObject.getConnectionHolder().getConnection();
try {
if (txObject.isMustRestoreAutoCommit()) {
// 恢复数据库连接的自功提交属性
con.setAutoCommit(true);
}
// reset 重置连接
DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
}
catch (Throwable ex) {
logger.debug("Could not reset JDBC Connection after transaction", ex);
}
if (txObject.isNewConnectionHolder()) {
if (logger.isDebugEnabled()) {
logger.debug("Releasing JDBC Connection [" + con + "] after transaction");
}
// 如果当前事务是独立的新创建的事务则在事务完成时释放数据库连接
DataSourceUtils.releaseConnection(con, this.dataSource);
}
txObject.getConnectionHolder().clear();
}
如果在事务执行前有事务挂起,那么当前事务执行结束后需要将挂起事务恢复。
protected final void resume(@Nullable Object transaction, @Nullable SuspendedResourcesHolder resourcesHolder)
throws TransactionException {
if (resourcesHolder != null) {
Object suspendedResources = resourcesHolder.suspendedResources;
if (suspendedResources != null) {
doResume(transaction, suspendedResources);
}
List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
// 恢复上一个事务的属性
if (suspendedSynchronizations != null) {
TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
doResumeSynchronization(suspendedSynchronizations);
}
}
}
事务提交
之前我们分析了 Spring 的事务异常处理机制,那么事务的执行并没有出现任何的异常, 也就意味着事务可以走正常事务提交的流程了。
protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
if (txInfo != null && txInfo.getTransactionStatus() != null) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
在真正的数据提交之前, 还需要做个判断。 不知道大家还有没有印象, 在我们分析事务异常处理规则的时候,当某个事务既没有保存点又不是新事务, Spring 对它的处理方式只是设置一个回滚标识。 这个回滚标识在这里就会派上用场了 ,主要的应用场景如下:
某个事务是另一个事务的嵌入事务, 但是, 这些事务又不在 Spring 的管理范围内, 或者无法设置保存点, 那么 Spring 会通过设置回滚标识的方式来禁止提交。 首先当某个嵌入事务发生 回滚的时候会设置凹滚标识,而等到外部事务提交时, 一旦判断出当前事务流被设置了回滚标识, 则由外部事务来统一进行整体事务的回滚。
所以,当事务没有被异常捕获的时候也并不意味着一定会执行提交的过程。
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()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus, false);
return;
}
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus, true);
return;
}
// 处理事务提交
processCommit(defStatus);
}
而当事务执行一切都正常的时候,便可以真正地进入提交流程了
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
// 预留
prepareForCommit(status);
// 添加的 TransactionSynchronization 中的对应方法的调用
triggerBeforeCommit(status);
// 添加的 TransactionSynchronization 中的对应方法的调用
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
// 如果存在保存点则消除保存点信息
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
unexpectedRollback = status.isGlobalRollbackOnly();
status.releaseHeldSavepoint();
}
// 如果是独立的事务则直接提交
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
unexpectedRollback = status.isGlobalRollbackOnly();
// 直接提交
/**
* 在提交过程中也并不是直接提交的 ,
* 而是考虑了诸多的方面,符合提交的条件如下。
* 1.当事务状态 中有保存点信息的话俊不会去才是交事务。
* 2.当事务非新事务的时候也不会去执行提交事务操作。
* 此条件主要考虑内嵌事务的情况,
* 对于内嵌事务,在 Spring 中正常的处理方式是将内嵌事务开始之前设置保存点,
* 一旦内嵌事务出现异常便根据保存点信息进行回滚,
* 但是如果没有出现异常,内嵌事务并不会单独提交,而是根据事务流由最外层事务负责提交,
* 所以如果当前存在保存点信息便不是最外层事务, 不做保存操作,对于是否是新事务的判断也是基于此考虑。
* 如果程序流通过了事务的层层把关,最后顺利地进入了提交流程,
* 那么同样,Spring 会将事务提交的操作引导至底层数据库连接的 API, 进行事务提交。
*/
doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
// can only be caused by doCommit
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
// can only be caused by doCommit
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException | Error ex) {
if (!beforeCompletionInvoked) {
// 添加的 TransactionSynchronization 中的对应方法的调用
triggerBeforeCompletion(status);
}
// 提交过程中出现异常则回滚
doRollbackOnCommitException(status, ex);
throw ex;
}
// Trigger afterCommit callbacks, with an exception thrown there
// propagated to callers but the transaction still considered as committed.
try {
// 添加的 TransactionSynchronization 中的对应方法的调用
triggerAfterCommit(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
finally {
cleanupAfterCompletion(status);
}
}
可以看到这里进行了非常多的异常处理,所以在提交过程中也并不是直接提交的 , 而是考虑了诸多的方面, 符合提交的条件如下。
当事务状态 中有保存点信息的话俊不会去才是交事务。
当事务非新事务的时候也不会去执行提交事务操作。
此条件主要考虑内嵌事务的情况,对于内嵌事务,在 Spring 中正常的处理方式是将内嵌事 务开始之前设置保存点, 一旦内嵌事务出现异常便根据保存点信息进行回滚,但是如果没有出 现异常,内嵌事务并不会单独提交, 而是根据事务流由最外层事务负责提交, 所以如果当前存 在保存点信息便不是最外层事务, 不做保存操作,对于是否是新事务的判断也是基于此考虑。 如果程序流通过了事务的层层把关,最后顺利地进入了提交流程, 那么同样, Spring 会将 事务提交的操作引导至底层数据库连接的 API, 进行事务提交。
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Committing JDBC transaction on Connection [" + con + "]");
}
try {
con.commit();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
}
到这里,事务的总体逻辑已经走完了。可以看到最终底层操作都是交给数据库去完成,而Spring则是负责各种细节的兜底处理,所以代码里会有非常多的异常捕获和日志打印。
总结
这一篇主要解析了事务具体实现的代码,细节非常的多,我写的也很乱。很容易就陷入细节里面无法自拔,所以写到最后也只是走马观花,简直是浪费生命。
水归水,还是要总结一下问题的。首先这篇文章是以声明式事务为例子分析,编程式事务没怎么用过,以后有机会再补充。然后我介绍了事务执行的流程,以及各种操作。当然细节很多,也没有面面俱到,实在是能力不足。
这应该是Aop系列的最后一篇了,完结撒花。
水平有限,如有错漏,还请指出。
如果有人看到这里,那在这里老话重提。与君共勉,路漫漫其修远兮,吾将上下而求索。
老生常谈系列之Aop--Aop的经典应用之Spring的事务实现分析(三)的更多相关文章
- 老生常谈系列之Aop--Spring Aop源码解析(一)
老生常谈系列之Aop--Spring Aop源码解析(一) 前言 上一篇文章老生常谈系列之Aop--Spring Aop原理浅析大概阐述了动态代理的相关知识,并且最后的图给了一个Spring Aop实 ...
- 老生常谈系列之Aop--Spring Aop源码解析(二)
老生常谈系列之Aop--Spring Aop源码解析(二) 前言 上一篇文章老生常谈系列之Aop--Spring Aop源码解析(一)已经介绍完Spring Aop获取advice切面增强方法的逻辑, ...
- 老生常谈系列之Aop--Spring Aop原理浅析
老生常谈系列之Aop--Spring Aop原理浅析 概述 上一篇介绍了AspectJ的编译时织入(Complier Time Weaver),其实AspectJ也支持Load Time Weaver ...
- 老生常谈系列之Aop--CGLIB动态代理的底层实现原理
老生常谈系列之Aop--CGLIB动态代理的底层实现原理 前言 上一篇老生常谈系列之Aop--JDK动态代理的底层实现原理简单讲解了JDK动态代理的实现,动态代理常用实现里面的双子星还有另一位--CG ...
- 老生常谈系列之Aop--JDK动态代理的底层实现原理
老生常谈系列之Aop--JDK动态代理的底层实现原理 前言 在Aop系列里面有两篇文章,分别是老生常谈系列之Aop--Spring Aop原理浅析和老生常谈系列之Aop--Spring Aop源码解析 ...
- Spring Ioc源码分析系列--Bean实例化过程(二)
Spring Ioc源码分析系列--Bean实例化过程(二) 前言 上篇文章Spring Ioc源码分析系列--Bean实例化过程(一)简单分析了getBean()方法,还记得分析了什么吗?不记得了才 ...
- Spring系列之aAOP AOP是什么?+xml方式实现aop+注解方式实现aop
Spring系列之aop aop是什么?+xml方式实现aop+注解方式实现aop 什么是AOP? AOP为Aspect Oriented Programming 的缩写,意识为面向切面的编程,是通过 ...
- 框架源码系列十:Spring AOP(AOP的核心概念回顾、Spring中AOP的用法、Spring AOP 源码学习)
一.AOP的核心概念回顾 https://docs.spring.io/spring/docs/5.1.3.RELEASE/spring-framework-reference/core.html#a ...
- 老生常谈系列之Aop--AspectJ
老生常谈系列之Aop--AspectJ 这篇文章的目的是大概讲解AspectJ是什么,所以这个文章会花比较长的篇幅去解释一些概念(这对于日常开发来说没一点卵用,但我就是想写),本文主要参考Aspect ...
随机推荐
- producer内存管理分析
1 概述 kafka producer调用RecordAccumulator#append来将消息存到本地内存.消息以TopicPartition为key分组存放,每个TopicPartition对应 ...
- Java中带参数的方法和JavaScript中带参数的函数有什么不同?
javascript是动态语言,是弱类型语言,其参数的使用很灵活:java则是强类型语言,参数的类型必须明确的
- Spring Bean生命周期回调
参阅官方文档:https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory ...
- zookeeper 负载均衡和 nginx 负载均衡区别?
zk 的负载均衡是可以调控,nginx 只是能调权重,其他需要可控的都需要自己写插件:但是 nginx 的吞吐量比 zk 大很多,应该说按业务选择用哪种方式.
- 区分 BeanFactory 和 ApplicationContext?
BeanFactory ApplicationContext 它使用懒加载 它使用即时加载 它使用语法显式提供资源对象 它自己创建和管理资源对象 不支持国际化 支持国际化 不支持基于依赖的注解 支持基 ...
- 学习DNS(二)
DNS出现及演化 网络出现的早期 是使用IP地址通讯的,那时就几台主机通讯.但是随着接入网络主机的增多,这种数字标识的地址非常不便于记忆,UNIX上就出现了建立一个叫做hosts的文件(Linux和w ...
- Netty学习摘记 —— 再谈EventLoop 和线程模型
本文参考 本篇文章是对<Netty In Action>一书第七章"EventLoop和线程模型"的学习摘记,主要内容为线程模型的概述.事件循环的概念和实现.任务调度和 ...
- Linux下切换python2和python3
为什么需要有两个版本的Python Python2和Python3不兼容是每个接触过Python的开发者都知道的事,虽说Python3是未来,但是仍然有很多项目采用Python2开发.Linux的许多 ...
- 【动态系统的建模与分析】9_一阶系统的频率响应_低通滤波器_Matlab/Simulink分析
magnitude response:振幅响应 phase response:相位响应 传递函数G(S)为什么将S看成jw化成G(jw)通过[动态系统的建模与分析]8_频率响应_详细数学推导 G(jw ...
- HTTP权威指南:第二章
URL概览 前面提到,URL资源是HTTP协议所使用的寻找资源位置的定位符.分为三个部分,主要的结构是: 方案://服务器/路径 这种结构使得网络上的每一个资源都只有唯一的命名方法,从而使得浏览器可以 ...