Spring源码解析——事务增强器

正文

上一篇文章我们讲解了事务的Advisor是如何注册进Spring容器的,也讲解了Spring是如何将有配置事务的类配置上事务的,实际上也就是用了AOP那一套,也讲解了Advisor,pointcut验证流程,至此,事务的初始化工作都已经完成了,在之后的调用过程,如果代理类的方法被调用,都会调用BeanFactoryTransactionAttributeSourceAdvisor这个Advisor的增强方法,也就是我们还未提到的那个Advisor里面的advise,还记得吗,在自定义标签的时候我们将TransactionInterceptor这个Advice作为bean注册进IOC容器,并且将其注入进Advisor中,这个Advice在代理类的invoke方法中会被封装到拦截器链中,最终事务的功能都在advise中体现,所以我们先来关注一下TransactionInterceptor这个类吧。最全面的Java面试网站

TransactionInterceptor类继承自MethodInterceptor,所以调用该类是从其invoke方法开始的,首先预览下这个方法:

@Override
@Nullable
public Object invoke(final 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);
}

重点来了,进入invokeWithinTransaction方法:

@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);// 获取beanFactory中的transactionManagerfinal PlatformTransactionManager tm = determineTransactionManager(txAttr);// 构造方法唯一标识(类.方法,如:service.UserServiceImpl.save)final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);// 声明式事务处理if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {// 创建TransactionInfoTransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);Object retVal = null;try {// 执行原方法// 继续调用方法拦截器链,这里一般将会调用目标类的方法,如:AccountServiceImpl.save方法retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// 异常回滚completeTransactionAfterThrowing(txInfo, ex);// 手动向上抛出异常,则下面的提交事务不会执行// 如果子事务出异常,则外层事务代码需catch住子事务代码,不然外层事务也会回滚throw ex;}finally {// 消除信息cleanupTransactionInfo(txInfo);}// 提交事务commitTransactionAfterReturning(txInfo);return retVal;}else {final ThrowableHolder throwableHolder = new ThrowableHolder();try {// 编程式事务处理Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);try {return invocation.proceedWithInvocation();}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;}}
}

创建事务Info对象

我们先分析事务创建的过程。

protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {// If no name specified, apply method identification as transaction name.// 如果没有名称指定则使用方法唯一标识,并使用DelegatingTransactionAttribute封装txAttrif (txAttr != null && txAttr.getName() == null) {txAttr = new DelegatingTransactionAttribute(txAttr) {@Overridepublic String getName() {return joinpointIdentification;}};}TransactionStatus status = null;if (txAttr != null) {if (tm != null) {// 获取TransactionStatusstatus = tm.getTransaction(txAttr);}else {if (logger.isDebugEnabled()) {logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +"] because no transaction manager has been configured");}}}// 根据指定的属性与status准备一个TransactionInforeturn prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

对于createTransactionlfNecessary函数主要做了这样几件事情。

(1)使用 DelegatingTransactionAttribute 封装传入的 TransactionAttribute 实例。

对于传入的TransactionAttribute类型的参数txAttr,当前的实际类型是RuleBasedTransactionAttribute,是由获取事务属性时生成,主要用于数据承载,而这里之所以使用DelegatingTransactionAttribute进行封装,当然是提供了更多的功能。

(2)获取事务。

事务处理当然是以事务为核心,那么获取事务就是最重要的事情。

(3)构建事务信息。

根据之前几个步骤获取的信息构建Transactionlnfo并返回。

分享一份大彬精心整理的大厂面试手册,包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等高频面试题,非常实用,有小伙伴靠着这份手册拿过字节offer~

需要的小伙伴可以自行下载

http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ==&mid=2247485445&idx=1&sn=1c6e224b9bb3da457f5ee03894493dbc&chksm=ce98f543f9ef7c55325e3bf336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd

获取事务

其中核心是在getTransaction方法中:

@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {// 获取一个transactionObject transaction = doGetTransaction();boolean debugEnabled = logger.isDebugEnabled();if (definition == null) {definition = new DefaultTransactionDefinition();}// 如果在这之前已经存在事务了,就进入存在事务的方法中if (isExistingTransaction(transaction)) {return handleExistingTransaction(definition, transaction, debugEnabled);}// 事务超时设置验证if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());}// 走到这里说明此时没有存在事务,如果传播特性是MANDATORY时抛出异常if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");}// 如果此时不存在事务,当传播特性是REQUIRED或NEW或NESTED都会进入if语句块else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {// PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED都需要新建事务// 因为此时不存在事务,将null挂起SuspendedResourcesHolder suspendedResources = suspend(null);if (debugEnabled) {logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);}try {boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);// new一个status,存放刚刚创建的transaction,然后将其标记为新事务!// 这里transaction后面一个参数决定是否是新事务!DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);// 新开一个连接的地方,非常重要doBegin(transaction, definition);prepareSynchronization(status, definition);return status;}catch (RuntimeException | Error ex) {resume(null, suspendedResources);throw ex;}}else {// Create "empty" transaction: no actual transaction, but potentially synchronization.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);}// 其他的传播特性一律返回一个空事务,transaction = null//当前不存在事务,且传播机制=PROPAGATION_SUPPORTS/PROPAGATION_NOT_SUPPORTED/PROPAGATION_NEVER,这三种情况,创建“空”事务boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);}
}

先来看看transaction是如何被创建出来的:

@Override
protected Object doGetTransaction() {// 这里DataSourceTransactionObject是事务管理器的一个内部类// DataSourceTransactionObject就是一个transaction,这里new了一个出来DataSourceTransactionObject txObject = new DataSourceTransactionObject();txObject.setSavepointAllowed(isNestedTransactionAllowed());// 解绑与绑定的作用在此时体现,如果当前线程有绑定的话,将会取出holder// 第一次conHolder肯定是nullConnectionHolder conHolder =(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());// 此时的holder被标记成一个旧holdertxObject.setConnectionHolder(conHolder, false);return txObject;
}

创建transaction过程很简单,接着就会判断当前是否存在事务:

@Override
protected boolean isExistingTransaction(Object transaction) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}public boolean hasConnectionHolder() {return (this.connectionHolder != null);
}

这里判断是否存在事务的依据主要是获取holder中的transactionActive变量是否为true,如果是第一次进入事务,holder直接为null判断不存在了,如果是第二次进入事务transactionActive变量是为true的(后面会提到是在哪里把它变成true的),由此来判断当前是否已经存在事务了。

至此,源码分成了2条处理线:

1.当前已存在事务:isExistingTransaction()判断是否存在事务,存在事务handleExistingTransaction()根据不同传播机制不同处理

2.当前不存在事务: 不同传播机制不同处理

当前不存在事务

如果不存在事务,传播特性又是REQUIRED或NEW或NESTED,将会先挂起null,这个挂起方法我们后面再讲,然后创建一个DefaultTransactionStatus ,并将其标记为新事务,然后执行doBegin(transaction, definition);这个方法也是一个关键方法

神秘又关键的status对象

TransactionStatus接口

public interface TransactionStatus extends SavepointManager, Flushable {// 返回当前事务是否为新事务(否则将参与到现有事务中,或者可能一开始就不在实际事务中运行)boolean isNewTransaction();// 返回该事务是否在内部携带保存点,也就是说,已经创建为基于保存点的嵌套事务。boolean hasSavepoint();// 设置事务仅回滚。void setRollbackOnly();// 返回事务是否已标记为仅回滚boolean isRollbackOnly();// 将会话刷新到数据存储区@Overridevoid flush();// 返回事物是否已经完成,无论提交或者回滚。boolean isCompleted();
}

再来看看实现类DefaultTransactionStatus

DefaultTransactionStatus

public class DefaultTransactionStatus extends AbstractTransactionStatus {//事务对象@Nullableprivate final Object transaction;//事务对象private final boolean newTransaction;private final boolean newSynchronization;private final boolean readOnly;private final boolean debug;//事务对象@Nullableprivate final Object suspendedResources;public DefaultTransactionStatus(@Nullable Object transaction, boolean newTransaction, boolean newSynchronization,boolean readOnly, boolean debug, @Nullable Object suspendedResources) {this.transaction = transaction;this.newTransaction = newTransaction;this.newSynchronization = newSynchronization;this.readOnly = readOnly;this.debug = debug;this.suspendedResources = suspendedResources;}//略...
}

我们看看这行代码 DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);

// 这里是构造一个status对象的方法
protected DefaultTransactionStatus newTransactionStatus(TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {boolean actualNewSynchronization = newSynchronization &&!TransactionSynchronizationManager.isSynchronizationActive();return new DefaultTransactionStatus(transaction, newTransaction, actualNewSynchronization,definition.isReadOnly(), debug, suspendedResources);
}

实际上就是封装了事务属性definition,新创建的**transaction,**并且将事务状态属性设置为新事务,最后一个参数为被挂起的事务。

简单了解一下关键参数即可:

第二个参数transaction:事务对象,在一开头就有创建,其就是事务管理器的一个内部类。

第三个参数newTransaction:布尔值,一个标识,用于判断是否是新的事务,用于提交或者回滚方法中,是新的才会提交或者回滚。

最后一个参数suspendedResources:被挂起的对象资源,挂起操作会返回旧的holder,将其与一些事务属性一起封装成一个对象,就是这个suspendedResources这个对象了,它会放在status中,在最后的清理工作方法中判断status中是否有这个挂起对象,如果有会恢复它

接着我们来看看关键代码 doBegin(transaction, definition);

 @Overrideprotected void doBegin(Object transaction, TransactionDefinition definition) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;Connection con = null;try {// 判断如果transaction没有holder的话,才去从dataSource中获取一个新连接if (!txObject.hasConnectionHolder() ||txObject.getConnectionHolder().isSynchronizedWithTransaction()) {//通过dataSource获取连接Connection newCon = this.dataSource.getConnection();if (logger.isDebugEnabled()) {logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");}// 所以,只有transaction中的holder为空时,才会设置为新holder// 将获取的连接封装进ConnectionHolder,然后封装进transaction的connectionHolder属性txObject.setConnectionHolder(new ConnectionHolder(newCon), true);}//设置新的连接为事务同步中txObject.getConnectionHolder().setSynchronizedWithTransaction(true);con = txObject.getConnectionHolder().getConnection();//conn设置事务隔离级别,只读Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);txObject.setPreviousIsolationLevel(previousIsolationLevel);//DataSourceTransactionObject设置事务隔离级别// 如果是自动提交切换到手动提交if (con.getAutoCommit()) {txObject.setMustRestoreAutoCommit(true);if (logger.isDebugEnabled()) {logger.debug("Switching JDBC Connection [" + con + "] to manual commit");}con.setAutoCommit(false);}// 如果只读,执行sql设置事务只读prepareTransactionalConnection(con, definition);// 设置connection持有者的事务开启状态txObject.getConnectionHolder().setTransactionActive(true);int timeout = determineTimeout(definition);if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {// 设置超时秒数txObject.getConnectionHolder().setTimeoutInSeconds(timeout);}// 将当前获取到的连接绑定到当前线程if (txObject.isNewConnectionHolder()) {TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());}}catch (Throwable ex) {if (txObject.isNewConnectionHolder()) {DataSourceUtils.releaseConnection(con, this.dataSource);txObject.setConnectionHolder(null, false);}throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);}}

conn设置事务隔离级别

@Nullable
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.0throw ex;}exToCheck = exToCheck.getCause();}// "read-only not supported" SQLException -> ignore, it's just a hint anywaylogger.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;
}

我们看到都是通过 Connection 去设置

线程变量的绑定

我们看 doBegin 方法的47行,**将当前获取到的连接绑定到当前线程,绑定与解绑围绕一个线程变量,此变量在TransactionSynchronizationManager**类中:

private static final ThreadLocal<Map<Object, Object>> resources =  new NamedThreadLocal<>("Transactional resources");

这是一个 static final 修饰的 线程变量,存储的是一个Map,我们来看看47行的静态方法,bindResource

public static void bindResource(Object key, Object value) throws IllegalStateException {// 从上面可知,线程变量是一个Map,而这个Key就是dataSource// 这个value就是holderObject actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);Assert.notNull(value, "Value must not be null");// 获取这个线程变量MapMap<Object, Object> map = resources.get();// set ThreadLocal Map if none foundif (map == null) {map = new HashMap<>();resources.set(map);}// 将新的holder作为value,dataSource作为key放入当前线程Map中Object oldValue = map.put(actualKey, value);// Transparently suppress a ResourceHolder that was marked as void...if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {oldValue = null;}if (oldValue != null) {throw new IllegalStateException("Already value [" + oldValue + "] for key [" +actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");}    Thread.currentThread().getName() + "]");}// 略...
}

扩充知识点

这里再扩充一点,mybatis中获取的数据库连接,就是根据 dataSource 从ThreadLocal中获取的

以查询举例,会调用Executor#doQuery方法:

最终会调用DataSourceUtils#doGetConnection获取,真正的数据库连接,其中TransactionSynchronizationManager中保存的就是方法调用前,spring增强方法中绑定到线程的connection,从而保证整个事务过程中connection的一致性

我们看看TransactionSynchronizationManager.getResource(Object key)这个方法

@Nullable
public static Object getResource(Object key) {Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);Object value = doGetResource(actualKey);if (value != null && logger.isTraceEnabled()) {logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" +Thread.currentThread().getName() + "]");}return value;
}@Nullable
private static Object doGetResource(Object actualKey) {Map<Object, Object> map = resources.get();if (map == null) {return null;}Object value = map.get(actualKey);// Transparently remove ResourceHolder that was marked as void...if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {map.remove(actualKey);// Remove entire ThreadLocal if empty...if (map.isEmpty()) {resources.remove();}value = null;}return value;
}

就是从线程变量的Map中根据 DataSource获取 ConnectionHolder

已经存在的事务

前面已经提到,第一次事务开始时必会新创一个holder然后做绑定操作,此时线程变量是有holder的且avtive为true,如果第二个事务进来,去new一个transaction之后去线程变量中取holder,holder是不为空的且active是为true的,所以会进入handleExistingTransaction方法:

 private TransactionStatus handleExistingTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled)throws TransactionException {// 1.NERVER(不支持当前事务;如果当前事务存在,抛出异常)报错if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {throw new IllegalTransactionStateException("Existing transaction found for transaction marked with propagation 'never'");}// 2.NOT_SUPPORTED(不支持当前事务,现有同步将被挂起)挂起当前事务,返回一个空事务if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {if (debugEnabled) {logger.debug("Suspending current transaction");}// 这里会将原来的事务挂起,并返回被挂起的对象Object suspendedResources = suspend(transaction);boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);// 这里可以看到,第二个参数transaction传了一个空事务,第三个参数false为旧标记// 最后一个参数就是将前面挂起的对象封装进新的Status中,当前事务执行完后,就恢复suspendedResourcesreturn prepareTransactionStatus(definition, null, false, newSynchronization, debugEnabled, suspendedResources);}// 3.REQUIRES_NEW挂起当前事务,创建新事务if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {if (debugEnabled) {logger.debug("Suspending current transaction, creating new transaction with name [" +definition.getName() + "]");}// 将原事务挂起,此时新建事务,不与原事务有关系// 会将transaction中的holder设置为null,然后解绑!SuspendedResourcesHolder suspendedResources = suspend(transaction);try {boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);// new一个status出来,传入transaction,并且为新事务标记,然后传入挂起事务DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);// 这里也做了一次doBegin,此时的transaction中holer是为空的,因为之前的事务被挂起了// 所以这里会取一次新的连接,并且绑定!doBegin(transaction, definition);prepareSynchronization(status, definition);return status;}catch (RuntimeException beginEx) {resumeAfterBeginException(transaction, suspendedResources, beginEx);throw beginEx;}catch (Error beginErr) {resumeAfterBeginException(transaction, suspendedResources, beginErr);throw beginErr;}}// 如果此时的传播特性是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'");}if (debugEnabled) {logger.debug("Creating nested transaction with name [" + definition.getName() + "]");}// 这里如果是JTA事务管理器,就不可以用savePoint了,将不会进入此方法if (useSavepointForNestedTransaction()) { // 这里不会挂起事务,说明NESTED的特性是原事务的子事务而已// new一个status,传入transaction,传入旧事务标记,传入挂起对象=nullDefaultTransactionStatus status =prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);// 这里是NESTED特性特殊的地方,在先前存在事务的情况下会建立一个savePointstatus.createAndHoldSavepoint();return status;}else {// JTA事务走这个分支,创建新事务boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, null);doBegin(transaction, definition);prepareSynchronization(status, definition);return status;}}// 到这里PROPAGATION_SUPPORTS 或 PROPAGATION_REQUIRED或PROPAGATION_MANDATORY,存在事务加入事务即可,标记为旧事务,空挂起boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);}

对于已经存在事务的处理过程中,我们看到了很多熟悉的操作,但是,也有些不同的地方,函数中对已经存在的事务处理考虑两种情况。

(1)PROPAGATION_REQUIRES_NEW表示当前方法必须在它自己的事务里运行,一个新的事务将被启动,而如果有一个事务正在运行的话,则在这个方法运行期间被挂起。而Spring中对于此种传播方式的处理与新事务建立最大的不同点在于使用suspend方法将原事务挂起。 将信息挂起的目的当然是为了在当前事务执行完毕后在将原事务还原。

(2)PROPAGATION_NESTED表示如果当前正有一个事务在运行中,则该方法应该运行在一个嵌套的事务中,被嵌套的事务可以独立于封装事务进行提交或者回滚,如果封装事务不存在,行为就像PROPAGATION_REQUIRES_NEW。对于嵌入式事务的处理,Spring中主要考虑了两种方式的处理。

  • Spring中允许嵌入事务的时候,则首选设置保存点的方式作为异常处理的回滚。
  • 对于其他方式,比如JTA无法使用保存点的方式,那么处理方式与PROPAGATION_ REQUIRES_NEW相同,而一旦出现异常,则由Spring的事务异常处理机制去完成后续操作。

对于挂起操作的主要目的是记录原有事务的状态,以便于后续操作对事务的恢复

小结

到这里我们可以知道,在当前存在事务的情况下,根据传播特性去决定是否为新事务,是否挂起当前事务。

NOT_SUPPORTED :会挂起事务,不运行doBegin方法传空transaction,标记为旧事务。封装status对象:

return prepareTransactionStatus(definition, null, false, newSynchronization, debugEnabled, suspendedResources)

REQUIRES_NEW :将会挂起事务且运行doBegin方法,标记为新事务。封装status对象:

DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);

NESTED :不会挂起事务且不会运行doBegin方法,标记为旧事务,但会创建savePoint。封装status对象:

DefaultTransactionStatus status =prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);

其他事务例如REQUIRED :不会挂起事务,封装原有的transaction不会运行doBegin方法,标记旧事务,封装status对象:

return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);

挂起

对于挂起操作的主要目的是记录原有事务的状态,以便于后续操作对事务的恢复:

@Nullable
protected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {if (TransactionSynchronizationManager.isSynchronizationActive()) {List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();try {Object suspendedResources = null;if (transaction != null) {// 这里是真正做挂起的方法,这里返回的是一个holdersuspendedResources = doSuspend(transaction);}// 这里将名称、隔离级别等信息从线程变量中取出并设置对应属性为null到线程变量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);// 将事务各个属性与挂起的holder一并封装进SuspendedResourcesHolder对象中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;}
}
@Override
protected Object doSuspend(Object transaction) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;// 将transaction中的holder属性设置为空txObject.setConnectionHolder(null);// ConnnectionHolder从线程变量中解绑!return TransactionSynchronizationManager.unbindResource(obtainDataSource());
}

我们来看看 unbindResource

private static Object doUnbindResource(Object actualKey) {// 取得当前线程的线程变量MapMap<Object, Object> map = resources.get();if (map == null) {return null;}// 将key为dataSourece的value移除出Map,然后将旧的Holder返回Object value = map.remove(actualKey);// Remove entire ThreadLocal if empty...// 如果此时map为空,直接清除线程变量if (map.isEmpty()) {resources.remove();}// Transparently suppress a ResourceHolder that was marked as void...if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {value = null;}if (value != null && logger.isTraceEnabled()) {logger.trace("Removed value [" + value + "] for key [" + actualKey + "] from thread [" +Thread.currentThread().getName() + "]");}// 将旧Holder返回return value;
}

可以回头看一下解绑操作的介绍。这里挂起主要干了三件事:

  1. 将transaction中的holder属性设置为空
  2. 解绑(会返回线程中的那个旧的holder出来,从而封装到SuspendedResourcesHolder对象中)
  3. 将SuspendedResourcesHolder放入status中,方便后期子事务完成后,恢复外层事务

最后给大家分享一个Github仓库,上面有大彬整理的300多本经典的计算机书籍PDF,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生等,可以star一下,下次找书直接在上面搜索,仓库持续更新中~

Github地址

如果访问不了Github,可以访问码云地址。

码云地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/161625.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CAD图形导出为XAML实践

文章目录 一、前言二、方法与实践2.1 画出原图&#xff0c;借第三方工具导出至指定格式2.2 CAD导出并转换2.3 两种方法的优劣2.3.1 直接导出代码量大2.3.2 导入导出需要调参 三、总结 一、前言 上位机通常有一个设备/场景界面&#xff0c;该界面用于清晰直观地呈现设备状态。 …

Yakit工具篇:子域名收集的配置和使用

简介(来自官方文档) 子域名收集是指通过各种技术手段&#xff0c;收集某个主域名下所有的子域名列表。子域名是指在主域名前面添加一级或多级名称的域名。例如&#xff0c;对于主域名example.com&#xff0c;其子域名可以是www.example.com、mail.example.com、blog.example.c…

触控笔哪个牌子好用?主动电容笔和被动电容笔的区别

主动式电容笔和被动式电容笔两者最大的不同之处在于主动式电容笔的应用范围更大&#xff0c;可以兼容各种不同的电容屏幕。随着人们对其认识的不断深入&#xff0c;其应用范围也在不断扩大。而且国产的主动式电容笔&#xff0c;也在不断的更新换代&#xff0c;重力越来越多&…

Django使用Token认证(simplejwt库的配置)

目录 官网文档安装项目配置拓展配置 官网文档 https://django-rest-framework-simplejwt.readthedocs.io/en/latest/ 安装 pip install djangorestframework-simplejwt项目配置 REST_FRAMEWORK {...DEFAULT_AUTHENTICATION_CLASSES: (...rest_framework_simplejwt.authent…

c#设计模式-行为型模式 之 访问者模式

&#x1f680;简介 封装一些作用于某种数据结构中的各元素的操作&#xff0c;它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。 访问者模式包含以下主要角色 : 抽象访问者&#xff08;Visitor&#xff09;角色&#xff1a;定义了对每一个元素 &#xff08;E…

pytorch3D Windows下安装经验总结

一、说明及准备工作 最近在安装pytorch3D的时候遇到了很多问题&#xff0c;查了很多博客&#xff0c;但发现讲的都不太全&#xff0c;所以特将自己的及收集到的安装过程经验总结如下。我是在Anaconda中虚拟环境下安装的。 1.1准备工作 官方安装教程如下&#xff1a;https://…

英特尔 SGX 技术概述

目录 介绍概述指示结构Memory安全区页面缓存Enclave Page Cache &#xff08;EPC&#xff09;安全区页面缓存映射Enclave Page Cache Map (EPCM) Memory ManagementStructures页面信息Page Information (PAGEINFO)安全信息Security Information (SECINFO)分页加密元数据Paging …

小程序入门——详细教程

&#x1f3ac; 艳艳耶✌️&#xff1a;个人主页 &#x1f525; 个人专栏 &#xff1a;《Spring与Mybatis集成整合》《Vue.js使用》 ⛺️ 生活的理想&#xff0c;为了不断更新自己 ! 1.微信小程序 入门 1.1什么是小程序&#xff1f; 2017年度百度百科十大热词之一 微信小程…

小程序的console中出现:。。。不在以下 request 合法域名列表中,请参考文档:。。。的报错解决

报错效果&#xff1a; 其实这个报错不代表自己的代码有问题 但是本强迫症研究了一下&#xff0c;按照以下方法关掉就不会显示这个报错了。 点微信开发者工具中的右上角的详情。点本地设置。勾选不校验。。。HTTPS证书。 即可关闭该报错&#xff1a;

java springboot VUE粮食经销系统开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 springboot VUE 粮食经销系统是一套完善的完整信息管理类型系统&#xff0c;结合springboot框架和VUE完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发&#xff09; &#xff0c;系统具有完整的源代码和数…

多域名证书

多域名证书是一种可以保护多个不同域名的SSL证书&#xff0c;最多可保护250个域名。与单域名证书相比&#xff0c;多域名证书具有更多的优势&#xff0c;比如可以更好地管理多个域名&#xff0c;减少部署证书的次数&#xff0c;提高网站安全性等。多域名证书有三种验证方式可以…

mac电脑安装雷蛇管理软件,实现调整鼠标dpi,移动速度,灯光等

雷蛇官网只给了win版本驱动 mac版本驱动到这里下载: GitHub - 1kc/razer-macos: Color effects manager for Razer devices for macOS. Supports High Sierra (10.13) to Monterey (12.0). Made by the community, based on openrazer. 安装后会显示开发者不明,请丢弃到垃圾桶.…

单片机综合小项目

一、单片机做项目常识 1.行业常识 2.方案选型 3.此项目定位和思路 二、单片机的小项目介绍 1.项目名称&#xff1a;基于51单片机的温度报警器 &#xff08;1&#xff09;主控&#xff1a;stc51&#xff1b; &#xff08;2&#xff09;编程语言&#xff1a;C语言 &#xff08;…

00TD时尚儿童穿搭,这件小熊毛衣太好看了吧

寒冷的秋冬季怎么少的了毛衣呢 软糯亲肤又时尚百搭的款谁不爱 除了纯色还有条纹设计 可爱小熊图案可爱又吸睛 经典时尚的款怎么穿都好看哦&#xff01;

css 特别样式记录

一、 这段代码神奇的地方在于&#xff0c; 本来容器的宽度只有1200px&#xff0c;如果不给img赋予宽度100%&#xff0c;那么图片 会超出盒子&#xff0c;如果给了img赋予了宽度100%&#xff0c;多个图片会根据自己图片大小的比例&#xff0c;去分完那1200px&#xff0c;如图二。…

Netty P1 NIO 基础,网络编程

Netty P1 NIO 基础&#xff0c;网络编程 教程地址&#xff1a;https://www.bilibili.com/video/BV1py4y1E7oA https://nyimac.gitee.io/2021/04/25/Netty%E5%9F%BA%E7%A1%80/ 1. 三大组件 1.1 Channel & Buffer Channel 类似 Stream&#xff0c;它是读写数据的双向通道…

华为9.20笔试 复现

第一题 丢失报文的位置 思路&#xff1a;从数组最小索引开始遍历 #include <iostream> #include <vector> using namespace std; // 求最小索引值 int getMinIdx(vector<int> &arr) {int minidx 0;for (int i 0; i < arr.size(); i){if (arr[i] …

【k8s总结】

资源下载&#xff1a;http://www.ziyuanwang.online/912.html Kubernetes(K8s) 一、Openstack&VM 1、认识虚拟化 1.1、什么是虚拟化 在计算机中&#xff0c;虚拟化&#xff08;英语&#xff1a;Virtualization&#xff09;是一种资源管理技术&#xff0c;是将计算机的…

力扣每日一题43:字符串相乘

题目描述&#xff1a; 给定两个以字符串形式表示的非负整数 num1 和 num2&#xff0c;返回 num1 和 num2 的乘积&#xff0c;它们的乘积也表示为字符串形式。 注意&#xff1a;不能使用任何内置的 BigInteger 库或直接将输入转换为整数。 示例 1: 输入: num1 "2"…

多模态及图像安全的探索与思考

前言 第六届中国模式识别与计算机视觉大会&#xff08;The 6th Chinese Conference on Pattern Recognition and Computer Vision, PRCV 2023&#xff09;已于近期在厦门成功举办。通过参加本次会议&#xff0c;使我有机会接触到许多来自国内外的模式识别和计算机视觉领域的研究…