一、先思考一下

  • 什么是事务?
    事务是一系列数据库操作的集合,在一个事务里,所有有关的数据库操作一起提交或一起回滚
  • 事务用在什么地方?
    如果多个数据库操作需要一起生效或一起失效,那么这些操作需要放在一个事务里面
  • 事务如何创建?
    用户创建了针对数据库操作的连接(java.sql.Connection)之后,就可以针对Connection进行事务的操作,事务依赖于连接
  • 事务的基本操作?
    1. 开启事务:Connection.setAutoCommit(false);关闭自动提交则就开启了事务
    2. 提交事务:Connection.commit();
    3. 回滚事务:Connection.rollback();

关于事务的各种概念:


更详细的参考

看源码之前,如果有看AOP源码的经历,会很有帮助,因为spring的事务就是基于AOP的

二、开启事务

  1. 基于XML

我们在beans.xml中开启事务的应用,需要添加

<tx:annotation-driven transaction-manager="transactionManager"/>
  • tx:annotation-driven/注解的分析

但凡这种注解,都有对应的解析器,跟AOP功能的源码一样,解析器都实现了NamespaceHandlerSupport类,我们来获取下NamespaceHandlerSupport的实现类都有哪些


看名字就是TxNamespaceHandler类,我们来看下这个类有哪些内容

  • TxNamespaceHandler
public class TxNamespaceHandler extends NamespaceHandlerSupport {...@Overridepublic void init() {registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());// 这句代码负责解析annotation-drivenregisterBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());}
}

Spring会默认调用其init()方法,annotation-driven对应的是AnnotationDrivenBeanDefinitionParser解析器,我们来看下这个解析器的作用

  • AnnotationDrivenBeanDefinitionParser的作用分析
public BeanDefinition parse(Element element, ParserContext parserContext) {registerTransactionalEventListenerFactory(parserContext);String mode = element.getAttribute("mode");if ("aspectj".equals(mode)) {// mode="aspectj"registerTransactionAspect(element, parserContext);}else {// 默认为proxy模式// 所以要执行该句AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);}return null;
}
  • AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);// txAdvisorBeanName值为 org.springframework.transaction.config.internalTransactionAdvisorString txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {Object eleSource = parserContext.extractSource(element);// 1.注册类AnnotationTransactionAttributeSource到Spring中RootBeanDefinition sourceDef = new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");sourceDef.setSource(eleSource);sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);// 2.注册类TransactionInterceptor到Spring中RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);interceptorDef.setSource(eleSource);interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registerTransactionManager(element, interceptorDef);interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);// 3.注册类BeanFactoryTransactionAttributeSourceAdvisor到Spring中RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);advisorDef.setSource(eleSource);advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);if (element.hasAttribute("order")) {advisorDef.getPropertyValues().add("order", element.getAttribute("order"));}parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName));parserContext.registerComponent(compositeDef);}
}

总结:通过以上的分析可知,tx:annotation-driven/的主要功能就是将以下四个类注册到Spring容器中

  • AnnotationTransactionAttributeSource
  • TransactionInterceptor(主要的拦截功能都在这里实现)
  • BeanFactoryTransactionAttributeSourceAdvisor(创建bean的代理类的时候该Advisor会被用上)
  • InfrastructureAdvisorAutoProxyCreator:用于创建事务代理
  • InfrastructureAdvisorAutoProxyCreator功能分析

其实现了BeanPostProcessor接口,则Spring在创建bean的时候,会默认调用InfrastructureAdvisorAutoProxyCreator的postProcessAfterInitialization()方法,然后就是wrapIfNecessary,跟AOP的流程完全一致

  1. 基于注解的@EnableTransactionManagement

可以看到,注册了AutoProxyRegistrar(是一个BeanPostProcessor)和ProxyTransactionManagementConfiguration(用于解析事务属性)

  • ProxyTransactionManagementConfiguration
  • 该类是一个配置Bean
  • 目的:创建事务的切面,跟AOP的区别就在这里,AOP的切面是我们自己定义的,而事务的切面是Spring给我们生成的
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {//想IOC容器中导入事务增强器(BeanFactoryTransactionAttributeSourceAdvisor),// 事务注解@Transactional的解析器(AnnotationTransactionAttributeSource)// 和事务方法拦截器(TransactionInterceptor);/**事务增强器(Advisor),在事务类创建的时候,被AutoProxyRegistrar导入的组件InfrastructureAdvisorAutoProxyCreator拦截,InfrastructureAdvisorAutoProxyCreator拦截的逻辑就是增强事务类的事务方法,   而BeanFactoryTransactionAttributeSourceAdvisor作为增强器,与需要增强的方法(这里是指被@Transactional标记的方法)进行匹配,匹配成功的增强器,最后转成拦截器(MethodInterceptor,就是下面的TransactionInterceptor),然后与目标方法一起在拦截器链中被执行,达到方法增强的效果;BeanFactoryTransactionAttributeSourceAdvisor的继承关系如下:BeanFactoryTransactionAttributeSourceAdvisor--AbstractBeanFactoryPointcutAdvisor--AbstractPointcutAdvisor--PointcutAdvisor--AdvisorAOP中AspectJPointcutAdvisor的继承关系如下,与AbstractPointcutAdvisor一样,都实现PointcutAdvisor--AspectJPointcutAdvisor--PointcutAdvisor--Advisor*/@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();advisor.setTransactionAttributeSource(transactionAttributeSource());advisor.setAdvice(transactionInterceptor());advisor.setOrder(this.enableTx.<Integer>getNumber("order"));return advisor;}/**@ Transactional注解的解析类;负责解析事务方法上@Transactional中的各个参数配置,解析的时机是在创建事务类之后被增强的时候,匹配事务方法的时候一起被解析了AnnotationTransactionAttributeSource的继承关系如下:AnnotationTransactionAttributeSource--AbstractFallbackTransactionAttributeSource--TransactionAttributeSource通过方法org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource.getTransactionAttribute(Method, Class<?>)解析出事务信息TransactionAttribute;AnnotationTransactionAttributeSource在方法findTransactionAttribute(Class<?>)中依赖于SpringTransactionAnnotationParser在解析事务类时,绑定事务方法与增强器的时候进行@Transactional注解解析;*/@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionAttributeSource transactionAttributeSource() {return new AnnotationTransactionAttributeSource();}/**被@Transactional标记的事务方法的拦截器,实际是一个MethodInterceptor保存了事务属性信息,事务管理器;在目标方法执行的时候;执行拦截器链;*/@Bean@Role(BeanDefinition.ROLE_INFRASTRUCTURE)public TransactionInterceptor transactionInterceptor() {TransactionInterceptor interceptor = new TransactionInterceptor();interceptor.setTransactionAttributeSource(transactionAttributeSource());if (this.txManager != null) {interceptor.setTransactionManager(this.txManager);}return interceptor;}
}
  • AutoProxyRegistrar


可以看到也注册了一个InfrastructureAdvisorAutoProxyCreator


跟xml的方式殊途同归:也实现了BeanPostProcessor接口,则Spring在创建bean的时候,会默认调用InfrastructureAdvisorAutoProxyCreator的postProcessAfterInitialization()方法,然后就是wrapIfNecessary,跟AOP的流程完全一致

那么后置处理bean有了,切面也有了,就可以创建事务代理

三、执行事务

跟AOP一样,以JdkDynamicProxy一样,调用代理,触发ReflectiveMethodInvocation的执行方法

retVal = invocation.proceed();

进而执行到

代码跟到这里,跟AOP又有一个不同的点,就是这个invoke的实现是TransactionInterceptor类,该类专门处理事务切面的执行

    //TransactionInterceptorpublic Object invoke(MethodInvocation invocation) throws Throwable {//计算出目标类:可能是 {@code null}。 TransactionAttributeSource 应该传递目标类以及方法,该方法可能来自接口。Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);//适配TransactionAspectSupport的invokeWithinTransaction...return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);}

进而进入invokeWithinTransaction

@Nullable//TransactionAspectSupportprotected 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();//拿到事务5个属性的值,@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT  )final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);//获取事务管理器,由我们通过配置指定。事务管理的底层一定会与数据库有关,所以会注入数据源等属性final PlatformTransactionManager tm = determineTransactionManager(txAttr);//切入点,也就是需要控制事务的目的方法(update...)final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {// Standard transaction demarcation with getTransaction and commit/rollback calls.//使用 getTransaction 和 commitRollback 调用进行标准事务划分,这一句是最难理解的//ifNecessary?其实就是根据事务属性决定是否开启事务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.// 源方法运行retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// target invocation exception//出现异常时则回滚事务,注意:如果是Exception不会回滚,只有RunTimeException或者Error来及其子类才会回滚completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {//重置 TransactionInfo ThreadLocal。 在所有情况下都调用它:异常或正常返回cleanupTransactionInfo(txInfo);}//提交事务//TransactionManager调用Connection.commit()commitTransactionAfterReturning(txInfo);return retVal;}

进入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) {txAttr = new DelegatingTransactionAttribute(txAttr) {@Overridepublic String getName() {return joinpointIdentification;}};}//记录当前事务的状态,是否新事务,是否只读事务,是否开启同步?TransactionStatus status = null;if (txAttr != null) {if (tm != null) {//获取事务的状态status = tm.getTransaction(txAttr);}else {if (logger.isDebugEnabled()) {logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +"] because no transaction manager has been configured");}}}return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);}

当TransactionAttribute为null,则创建一个TransactionInfo

protected TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm,@Nullable TransactionAttribute txAttr, String joinpointIdentification,@Nullable TransactionStatus status) {/** 根据给定的事务属性、事务管理器、方法连接点描述字符串(全限定方法名)信息创建一个事务信息对象TransactionInfo*/TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);// 如果事务属性不为nullif (txAttr != null) {// We need a transaction for this method...if (logger.isTraceEnabled()) {logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");}//那么设置事务状态,这里就表示为当前方法创建了一个事务txInfo.newTransactionStatus(status);}// 如果事务属性为null,那么表示当前方法必须要创建事务else {if (logger.isTraceEnabled()) {logger.trace("No need to create transaction for [" + joinpointIdentification +"]: This method is not transactional.");}}//始终将最新的TransactionInfo绑定到当前线程,即使我们没有在此处创建新的事务也是如此。//也就是将当前线程的最新事务栈设置为当前对象存入transactionInfoHolder中//这保证即使此方面未创建任何事务,也将正确管理TransactionInfo堆栈。txInfo.bindToThread();return txInfo;
}
  • 这里要先介绍一下TransactionInfo,这是SpringTX的核心,TransactionInfo是TransactionAspectSupport的内部类,用来保存线程的执行方法时的事务信息。内部保存了事务管理器transactionManager、事务属性transactionAttribute、全路径方法名joinpointIdentification。还保存了当前方法的事务transactionStatus,以及前一个方法的事务信息对象oldTransactionInfo。
 protected final class TransactionInfo {@Nullable//事务管理器private final PlatformTransactionManager transactionManager;@Nullable//事务属性private final TransactionAttribute transactionAttribute;//连接点描述字符串(方法的全限定名),主要用于记录日志private final String joinpointIdentification;@Nullable//事务状态private TransactionStatus transactionStatus;@Nullable//老的事务信息对象private TransactionInfo oldTransactionInfo;public TransactionInfo(@Nullable PlatformTransactionManager transactionManager,@Nullable TransactionAttribute transactionAttribute, String joinpointIdentification) {this.transactionManager = transactionManager;this.transactionAttribute = transactionAttribute;this.joinpointIdentification = joinpointIdentification;}public PlatformTransactionManager getTransactionManager() {Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");return this.transactionManager;}@Nullablepublic TransactionAttribute getTransactionAttribute() {return this.transactionAttribute;}/*** Return a String representation of this joinpoint (usually a Method call)* for use in logging.*///返回此连接点的String表示形式(通常是方法调用),以用于日志记录。public String getJoinpointIdentification() {return this.joinpointIdentification;}public void newTransactionStatus(@Nullable TransactionStatus status) {this.transactionStatus = status;}@Nullablepublic TransactionStatus getTransactionStatus() {return this.transactionStatus;}/*** Return whether a transaction was created by this aspect,* or whether we just have a placeholder to keep ThreadLocal stack integrity.*///返回是否是通过此切面创建的事务,或者是否只是一个占位符以保持ThreadLocal堆栈完整性。即当前方法是否新建了事务。public boolean hasTransaction() {return (this.transactionStatus != null);}private void bindToThread() {向线程公开当前的TransactionStatus,并保留所有现有的老的TransactionStatus,以在此事务完成后恢复。// Expose current TransactionStatus, preserving any existing TransactionStatus// for restoration after this transaction is complete.this.oldTransactionInfo = transactionInfoHolder.get();transactionInfoHolder.set(this);}private void restoreThreadLocalStatus() {// Use stack to restore old transaction TransactionInfo.// Will be null if none was set.// 使用堆栈还原旧的事务TransactionInfo。如果未设置,则为null。transactionInfoHolder.set(this.oldTransactionInfo);}@Overridepublic String toString() {return (this.transactionAttribute != null ? this.transactionAttribute.toString() : "No transaction");}}

如果事务属性不为空,那么走getTransaction获取/开启事务分支。在createTransactionIfNecessary方法中,如果存在事务属性TransactionAttribute,并且存在事务管理器PlatformTransactionManager,那么将调用事务管理器的getTransaction方法根据为当前方法配置的事务定义的属性尝试获取事务,将返回一个TransactionStatus对象。该方法是事务管理的核心方法,其骨干实现位于抽象实现类AbstractPlatformTransactionManager中,该方法根据配置的各种事务传播行为做出不同的处理,方法执行完毕将可能开启了新事物,也可能没有开启,甚至抛出异常。AbstractPlatformTransactionManager的设计基于模版方法模式,他提供了处理流程,并且提供了一系列的do……模版方法供子类来实现自己的逻辑。

getTransaction方法的大概逻辑为:

  1. 首先通过doGetTransaction方法获取一个内部事务对象,该方法由具体的事务管理器子类自己实现,返回的对象也是不一样的,对于DataSourceTransactionManager这里获取的实际上是一个DataSourceTransactionObject,其内部可能会包含当前数据的连接以及当前的事务信息,也可能没有(比如首次进入事务方法)
  2. 通过isExistingTransaction方法判断事物对象内部的数据库连接connectionHolder是否不为null并且是否已经开启过了事务。
    • 如果条件都满足,那么表示此前已经开启过了事务,即存在外层事务,随后调用handleExistingTransaction方法统一处理这种情况,比如加入当前事务、新建事务、抛出异常等等逻辑。
  1. 如果不都满足,即不存在外层事务,那么说明当前的事务方法就是最外层的事务方法,那么走下面的逻辑。
    • 校验为当前事务方法设置的事务超时时间,如果小于默认超时时间(-1),将会抛出异常。
    • 如果为当前事务方法设置的传播行为PROPAGATION_MANDATORY,该传播行为的含义是:如果当前存在事务,则当前方法加入到该事务中去,如果当前不存在事务,则当前方法直接抛出异常。这里由于不存在外层事务,那么这里就直接抛出异常:“No existing transaction found for transaction marked with propagation ‘mandatory’”。
    • 如果为当前事务方法设置的传播行为是PROPAGATION_REQUIRED或者PROPAGATION_REQUIRES_NEW或者PROPAGATION_NESTED,这些传播行为的含义的共同点之一就是:如果当前不存在事务,就创建一个新事务运行。这里由于不存在外层事务,那么这里就直接创建一个新事物。
    • 首先调用suspend方法首先挂起事务同步,然后再委派给doSuspend模板方法挂起事务,将返回被挂起的资源,用于后续恢复,如果没有事务同步也没有事务,那么将返回null。这里由于没有已存在的事务,那么参数传递null,一般也会将返回null。
    • 调用startTransaction方法,内部依次调用newTransactionStatus方法创建TransactionStatus、doBegin方法真正的开启新的事务、prepareSynchronization方法准备事务同步,最终返回TransactionStatus,该对象包含了创建的内部事务对象,以及其他事务信息。
  1. 否则,配置的事务的传播行为就是剩下的三种:PROPAGATION_SUPPORTS或PROPAGATION_NEVER或PROPAGATION_NOT_SUPPORTED,这几个传播行为的含义的共同点之一就是:当前方法一定以非事务的方式运行。那么这里仍然会创建TransactionStatus对象,但是不会开启事物,相当于一个空事务。

    • 调用prepareTransactionStatus方法返回一个TransactionStatus,和上面的startTransaction方法相比,其内部会调用newTransactionStatus和prepareSynchronization,但不会调用doBegin方法,因此不会真正的开启事物。这里它的newTransaction参数为false,suspendedResources参数为null。
@Overridepublic final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {//获取当前事务,该方法由具体的事务管理器子类自己实现,比如DataSourceTransactionManager、JtaTransactionManager//一般都使用DataSourceTransactionManager这个事务管理器//对于DataSourceTransactionManager这里获取的实际上是一个数据库的事务连接对象,即DataSourceTransactionObjectObject transaction = doGetTransaction();// Cache debug flag to avoid repeated checks.boolean debugEnabled = logger.isDebugEnabled();if (definition == null) {// Use defaults if no transaction definition given.definition = new DefaultTransactionDefinition();}// isExistingTransaction,默认返回false,同样被具体的事务管理器子类重写// DataSourceTransactionManager的方法将会判断上面获取的DataSourceTransactionObject内部的数据库连接connectionHolder属性是否不为null,// 并且是否已经开启了事务。我们说过如果当前线程是第一次进来,那么connectionHolder就是null。if (isExistingTransaction(transaction)) {// Existing transaction found -> check propagation behavior to find out how to behave.//如果已经存在事务,那么将检查传播行为并进行不同的处理,随后返回return handleExistingTransaction(definition, transaction, debugEnabled);}//到这里表示没有已存在的事务,进入第一个事务方法时将会走这个逻辑//设置的事务超时时间如果小于默认超时时间(-1),将会抛出异常// Check definition settings for new transaction.if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());}//如果配置的事务的传播行为是PROPAGATION_MANDATORY,该传播行为的含义是://如果当前存在事务,则当前方法加入到该事务中去,如果当前不存在事务,则当前方法直接抛出异常。//这里就直接抛出异常。// No existing transaction found -> check propagation behavior to find out how to proceed.if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");}//否则,如果配置的事务的传播行为是PROPAGATION_REQUIRED或者PROPAGATION_REQUIRES_NEW或者PROPAGATION_NESTED,//这几个传播行为的含义的共同点之一就是:如果当前不存在事务,就创建一个新事务运行。//那么这里将开启一个新事物。else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {//暂停给定的事务。首先挂起事务同步,然后再委派给doSuspend模板方法。由于此前没有事务,所以参数事务为nullSuspendedResourcesHolder suspendedResources = suspend(null);if (debugEnabled) {logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);}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 ex) {//唤醒此前挂起的事务和资源resume(null, suspendedResources);throw ex;}}//否则,配置的事务的传播行为就是剩下的三种:PROPAGATION_SUPPORTS或PROPAGATION_NEVER或PROPAGATION_NOT_SUPPORTED,//这几个传播行为的含义的共同点之一就是:当前方法一定以非事务的方式运行。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);}boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);//为给定参数创建一个新的TransactionStatus,并在适当时初始化事务同步,但是不会真正开启事务。//和startTransaction相比,其内部会调用newTransactionStatus和prepareSynchronization,但不会调用doBegin方法return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);}}
  • doGetTransaction获取事务连接

返回当前已存在的事务对象,返回的对象应包含有关任何现有事务的信息,即,在事务管理器上的当前getTransaction方法调用之前已经启动的事务。因此,doGetTransaction的实现通常是将查找现有事务并将相应的状态存储在返回的事务对象中。返回的对象通常特定于具体的事务管理器子类自己实现。一般都使用DataSourceTransactionManager这个事务管理器,它的doGetTransaction方法逻辑如下:

  1. 创建一个DataSourceTransactionObject对象,由DataSourceTransactionManager用作内部事务对象。可能会持有一个ConnectionHolder对象,还具有创建、回滚、释放保存点的功能
  2. 设置是否允许保存点,DataSourceTransactionManager默认会允许,一般用于实现嵌套事务。
  3. obtainDataSource方法用于获取配置的数据源,就是我们自己配置的数据源,getResource用于获取此线程在当前数据源中已拥有JDBC连接资源持有者。如果此前没有获取过连接,则返回null;如果此前开启了过事务(外层事务),那么肯定不会获取null。
  4. 设置连接信息,newConnectionHolder属性设置为false,这表示默认此前已经存在ConnectionHolder,但实际上可能并没有,因此后面的步骤中会再次判断该值。
  5. 最后返回DataSourceTransactionObject
@Override//DataSourceTransactionManagerprotected Object doGetTransaction() {//创建一个DataSourceTransactionObject,由DataSourceTransactionManager用作内部事务对象。//内部可能会持有一个ConnectionHolder对象,还具有创建、回滚、释放保存点的功能DataSourceTransactionObject txObject = new DataSourceTransactionObject();//设置是否允许保存点,DataSourceTransactionManager默认会允许,用于实现嵌套事务txObject.setSavepointAllowed(isNestedTransactionAllowed());//obtainDataSource方法用于获取配置的数据源,就是我们自己配置的数据源//getResource用于获取此线程在当前数据源中已拥有JDBC连接资源持有者ConnectionHolder//如果此前没有获取过连接,则返回null;如果此前开启了过事务(外层事务),那么肯定不会获取nullConnectionHolder conHolder =(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());//设置连接信息,newConnectionHolder属性设置为false,这表示默认此前已经存在ConnectionHolder//但实际上可能并没有,因此后面的步骤中会再次判断该值txObject.setConnectionHolder(conHolder, false);return txObject;}
  • 这里也要介绍一个类TransactionSynchronizationManager事务同步管理器

    这个类比较特别,虽然它的名字带有Transaction以及Manager,但却不是TransactionManager体系,它没有任何继承树,因此它可以看作一个很特别的工具类。该类被称为事务同步管理器。主要用于管理每一个线程当前所使用的数据库事务连接资源和事务同步器(TransactionSynchronization)
      一个线程在当前只能激活一个连接资源,因此如果需要绑定新的连接资源,那么需要将此前绑定的资源删除(或者说保存起来,等新资源使用完毕之后再恢复此前的连接资源)。另外还能支持事务同步列表,事务同步必须由事务管理器通过initSynchronization()和clearSynchronization()来进行激活和停用,使用isSynchronizationActive()检测当前是否具有事务同步
      TransactionSynchronizationManager内部是通过很多ThreadLocal(线程本地变量)类型的属性来实现为当前线程维护自己的资源的功能的,实现资源隔离。

部分属性:

public abstract class TransactionSynchronizationManager {private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);// 事务资源// 一个ThreadLocal属性,用于存放线程当前使用的数据库资源// value是一个Map<Object, Object>,key为某个数据源DataSource ,value实际上就是连接ConnectionHolderprivate static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<>("Transactional resources");/*** 事务同步* <p>* 一个ThreadLocal属性,用于存放线程当前激活的事务同步器TransactionSynchronization* 每个线程都可以开启多个事物同步,用于在处理事务的各个阶段进行自定义扩展或者回调* <p>* TransactionSynchronization的同步回调功能类似于此前学习的@TransactionalEventListener*/private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =new NamedThreadLocal<>("Transaction synchronizations");//一个ThreadLocal属性,用于存放线程当前的事务的名称private static final ThreadLocal<String> currentTransactionName =new NamedThreadLocal<>("Current transaction name");//一个ThreadLocal属性,用于存放线程当前的事务的只读状态private static final ThreadLocal<Boolean> currentTransactionReadOnly =new NamedThreadLocal<>("Current transaction read-only status");//一个ThreadLocal属性,用于存放线程当前的当前事务的隔离级别private static final ThreadLocal<Integer> currentTransactionIsolationLevel =new NamedThreadLocal<>("Current transaction isolation level");//一个ThreadLocal属性,用于存放线程当前是否开启了事务private static final ThreadLocal<Boolean> actualTransactionActive =new NamedThreadLocal<>("Actual transaction active");
  • obtainDataSource获取数据源

获取当前DataSourceTransactionManager实际使用的数据源,就是我们配置给事务管理器的DataSource

/*** @return 数据源(绝不为null)*/
protected DataSource obtainDataSource() {DataSource dataSource = getDataSource();Assert.state(dataSource != null, "No DataSource set");return dataSource;
}
/*** 就是我们配置的数据源*/
@Nullable
private DataSource dataSource;
/*** 返回此事务管理器内部的JDBC DataSource。*/
@Nullable
public DataSource getDataSource() {return this.dataSource;
}
  • getResource获取已存在的连接

TransactionSynchronizationManager的方法,该方法检索给定key绑定到当前线程的资源,实际上就是尝试从resources属性中获绑定当前线程的从给定数据源获已取到的连接资源ConnectionHolder。如果此前没有获取过此数据源的连接,那么将会得到一个null值,即如果是第一次进入事务方法,那么将返回null。这里resources属性的value是一个Map<Object, Object>,这说明一个线程可以从不同的数据源中获取资源,但是对于同一个数据源,只能保存一个的数据源。

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;}
  • isExistingTransaction是否已存在事务

检查是否已经开启过事务,默认返回false,被具体的事务管理器子类重写。
DataSourceTransactionManager的逻辑很简单,判断通过doGetTransaction方法获取的DataSourceTransactionObject内部的数据库连接connectionHolder属性是否不为null,并且是否已经开启了事务。我们说过如果当前线程是第一次进来,那么connectionHolder就是null。

@Overrideprotected boolean isExistingTransaction(Object transaction) {强转为DataSourceTransactionObjectDataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;判断内部的数据库连接connectionHolder是否不为null并且已经开启了事务return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());}
  • suspend挂起事务

挂起给定的事务。首先挂起当前线程的事务同步回调,然后再委派给doSuspend模板方法由子类来实现挂起当前事务,并且还会清空TransactionSynchronizationManager中保存的当前线程的事务信息。最终将会返回会被挂起的资源只有者SuspendedResourcesHolder。如果没有事务同步也没有事务,那么将会返回null。当前线程第一次进入事务方法时,默认将会返回null。

//AbstractPlatformTransactionManagerprotected final SuspendedResourcesHolder suspend(@Nullable Object transaction) throws TransactionException {//如果当前线程的事务同步处于活动状态,即存在绑定的TransactionSynchronization,则返回true。//如果是第一次因为进来,那么自然为falseif (TransactionSynchronizationManager.isSynchronizationActive()) {//挂起当前线程的所有事务同步回调,这类似于@TransactionalEventListener,并返回"被挂起"的回调List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization();try {Object suspendedResources = null;if (transaction != null) {//挂起事务,由具体的子类实现suspendedResources = doSuspend(transaction);}//获取当前事务的信息,并且清空各个ThreadLocal缓存中的当前线程的当前事务信息(恢复为默认值)//获取并清空(设置为null)事物名称String name = TransactionSynchronizationManager.getCurrentTransactionName();TransactionSynchronizationManager.setCurrentTransactionName(null);//获取并清空(设置为false)事物只读状态boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();TransactionSynchronizationManager.setCurrentTransactionReadOnly(false);//获取并清空(设置为null)事物隔离级别Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null);//获取并清空(设置为false)事物是否激活boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive();TransactionSynchronizationManager.setActualTransactionActive(false);//将获取的当前事物的信息存入一个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);//将挂起的资源存入一个SuspendedResourcesHolder对象中返回return new SuspendedResourcesHolder(suspendedResources);}else {// Neither transaction nor synchronization active.//事务或者事务同步均未激活,返回null,什么也不干return null;}}
  • isSynchronizationActive是否激活事务同步

    TransactionSynchronizationManager的方法,用来判断线程在当前是否已激活事务同步TransactionSynchronization。实际上就是synchronizations属性中是否有绑定到当前线程的Set集合,如果有(不为null),那就说明存在事务同步。
/*** TransactionSynchronizationManager的属性* <p>* 一个ThreadLocal类型的属性,每个线程都可以开启事物同步,用于在处理事务的各个阶段进行自定义扩展或者回调* TransactionSynchronization的同步回调功能类似于此前学习的@TransactionalEventListener*/
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =new NamedThreadLocal<>("Transaction synchronizations");
/*** TransactionSynchronizationManager的方法* <p>* 判断当前线程的是否注册了事务同步*/
public static boolean isSynchronizationActive() {//获取当前线程绑定的TransactionSynchronization,如果不为null就说明注册了事务同步return (synchronizations.get() != null);
}
  • doSuspendSynchronization挂起事务同步
      
      该方法挂起当前线程在TransactionSynchronizationManager的synchronize并且将属性中为当前线程保持的事务同步列表引用移除,最后返回被挂起的事务同步列表!
/*** AbstractPlatformTransactionManager的方法* <p>* 挂起所有当前同步,并停用当前线程的事务同步。** @return 挂起的TransactionSynchronization对象的列表*/
private List<TransactionSynchronization> doSuspendSynchronization() {//获取线程的当前的所有事务同步列表List<TransactionSynchronization> suspendedSynchronizations =TransactionSynchronizationManager.getSynchronizations();//遍历,依次挂起每一个事务同步for (TransactionSynchronization synchronization : suspendedSynchronizations) {synchronization.suspend();}//清除synchronizations属性中保存的的当前线程的当前事务同步集合的引用TransactionSynchronizationManager.clearSynchronization();//返回被挂起的事务同步return suspendedSynchronizations;
}/*** TransactionSynchronizationManager的方法* 调用该方法时一定要保证当前线程存在事务同步,否则将抛出异常* 因此需要先调用isSynchronizationActive方法来校验* <p>* 返回当前线程的所有已注册的事务同步的无法修改的快照列表** @return 无法修改的TransactionSynchronization实例列表* @throws IllegalStateException 如果同步未激活*/
public static List<TransactionSynchronization> getSynchronizations() throws IllegalStateException {//获取当前线程的事务同步列表Set<TransactionSynchronization> synchs = synchronizations.get();//为null就抛出IllegalStateException异常if (synchs == null) {throw new IllegalStateException("Transaction synchronization is not active");}// 返回不可修改的快照,以避免在迭代和调用可能进一步注册同步的同步回调时抛出ConcurrentModificationExceptions。if (synchs.isEmpty()) {return Collections.emptyList();} else {//在获取的之后对快照进行排序List<TransactionSynchronization> sortedSynchs = new ArrayList<>(synchs);AnnotationAwareOrderComparator.sort(sortedSynchs);return Collections.unmodifiableList(sortedSynchs);}
}/*** TransactionSynchronizationManager的方法* 调用该方法时一定要保证当前线程存在事务同步,否则将抛出异常* 因此需要先调用isSynchronizationActive方法来校验* <p>* 停用当前线程的事务同步,由事务管理器在事务清理中调用。** @throws IllegalStateException 如果同步未激活*/
public static void clearSynchronization() throws IllegalStateException {//如果没有激活事务同步,同样抛出异常if (!isSynchronizationActive()) {throw new IllegalStateException("Cannot deactivate transaction synchronization - not active");}logger.trace("Clearing transaction synchronization");//移除当前线程绑定到synchronizations属性的值synchronizations.remove();
}
  • doSuspend挂起事务

其核心就是doSuspend方法,该方法默认抛出异常,由子类自己实现!DataSourceTransactionManager重写的方法很简单,就是将DataSourceTransactionObject中的connectionHolder设置为null,并且将给定数据源绑定到当前线程的连接资源从TransactionSynchronizationManager的resources属性中移除并返回,这就是DataSourceTransactionManager被挂起的连接资源,就是此前获取的连接。从这里能够看出,所谓的“挂起”,就是将当前的连接从绑定的线程本地变量中移除!

/*** DataSourceTransactionManager的方法* <p>* 挂起当前事务,返回当前的连接资源** @param transaction 挂起事务* @return 被挂起的资源*/
@Override
protected Object doSuspend(Object transaction) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;//将当前的事务对象的connectionHolder设置为nulltxObject.setConnectionHolder(null);//将当前线程的绑定的当前数据源对应的连接同样移除,并且返回被移除的连接资源return TransactionSynchronizationManager.unbindResource(obtainDataSource());
}/*** TransactionSynchronizationManager的方法* <p>* 移除当前线程中给定key绑定的资源的值** @param key 就是当前数据源* @return 被移除的资源,就是连接*/
public static Object unbindResource(Object key) throws IllegalStateException {Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);//真正的移除指定的key对应的连接Object value = doUnbindResource(actualKey);if (value == null) {throw new IllegalStateException("No value for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");}return value;
}/*** 事务资源*/
private static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<>("Transactional resources");/*** TransactionSynchronizationManager的方法* <p>* 移除当前线程中给定key绑定的资源的值。** @param actualKey 就是当前数据源* @return 被移除的资源,就是连接*/
@Nullable
private static Object doUnbindResource(Object actualKey) {//获取和当前线程绑定的数据库资源mapMap<Object, Object> map = resources.get();if (map == null) {return null;}//从map中移除从当前数据源对应的连接缓存Object value = map.remove(actualKey);//如果map为空,则删除整个ThreadLocal。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() + "]");}//返回被移除的资源return value;
}
  • newTransactionStatus开启新事物

新建一个DefaultTransactionStatus实现并返回,内部持有我们为当前方法配置的事务属性或者默认属性,以及保存着此前挂起的其他资源。新建一个DefaultTransactionStatus实现并返回,内部持有我们为当前方法配置的事务属性或者默认属性,以及保存着此前挂起的其他资源

/*** AbstractPlatformTransactionManager的方法* <p>* 为给定参数新创建一个TransactionStatus实例,实际类型为DefaultTransactionStatus** @param definition         为当前方法配置的事务定义* @param transaction        获取的事务对象* @param newTransaction     是否是新事物* @param newSynchronization 是否开启事务同步* @param debug              是否支持debug级别的日志* @param suspendedResources 被挂起的资源,比如此前的事务同步* @return DefaultTransactionStatus对象*/
protected DefaultTransactionStatus newTransactionStatus(TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {//如果newSynchronization为true并且当前线程没有绑定的事务同步,那么确定开启新事物同步//由于此前调用了suspend方法清理了此前的事务同步,因此一般都是需要开启新事务同步,即为trueboolean actualNewSynchronization = newSynchronization &&!TransactionSynchronizationManager.isSynchronizationActive();//返回一个新建的DefaultTransactionStatus对象,该对象被用来表示新开启的事务,是TransactionStatus的默认实现//内部包括了各种新开启的事务状态,当然包括此前挂起的事务的资源return new DefaultTransactionStatus(transaction, newTransaction, actualNewSynchronization,definition.isReadOnly(), debug, suspendedResources);
}
/*DefaultTransactionStatus的属性*/
/*** 从事务管理器获取的内部事务* 对于DataSourceTransactionManager来说就是DataSourceTransactionObject*/
@Nullable
private final Object transaction;
/*** 是否是新事物*/
private final boolean newTransaction;
/*** 是否开启新事务同步*/
private final boolean newSynchronization;
/*** 是否开启新事务同步*/
private final boolean readOnly;
/*** 是否支持debug日志级别*/
private final boolean debug;
/*** 此前被挂起的事务资源*/
@Nullable
private 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;
}
  • doBegin真正开启事务

该方法是核心方法,当事务管理器决定实际开始新事务时,将调用此方法。此时之前可能没有任何事务,或者先前的事务已被暂停。根据给定的事务定义TransactionDefinition,以及此前通过doGetTransaction方法返回的事务对象(也就是DataSourceTransactionObject),使用给定的语义开始一个新事务。不必关心应用传播行为,因为抽象事务管理器已经处理了该行为。
  
大概步骤为:

  1. 从数据源中获取一个新连接Connection,并且包装为一个ConnectionHolder对象。ConnectionHolder设置为DataSourceTransactionObject事务对象的connectionHolder属性,并且将newConnectionHolder属性设置为true。
  2. 初始化连接的各种属性,比如隔离级别、只读标志等。
  3. 通过con.setAutoCommit(false),设置当前连接为手动提交,真正开启事务!
  4. 设置ConnectionHolder的transactionActive属性为true,表示激活当前连接的事务。
  5. 设置超时时间。如果不是默认超时时间-1,那么将根据设置的值和当前时间转换为未来的毫秒值并创建新Date配置给ConnectionHolder的deadline属性,在其他数据库操作框架操作时将会获取该参数
  6. 如果是新连接,即newConnectionHolder属性为true,绑定ConnectionHolder资源到TransactionSynchronizationManager的resources属性中,就是前面说的事务资源,key就是当前的属性源dataSource,value就是ConnectionHolder。
//开启新事物,并不会处理事务的传播行为,因为传播行为是Spring提供的特性,在事务管理器中就被直接处理了@Override//DataSourceTransactionManagerprotected 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");}/** 新建一个ConnectionHolder对象,其内部保存这获取的连接,* 将使用SimpleConnectionHandle包装获取的连接并且设置为connectionHandle属性** 该ConnectionHolder被设置给txObject的connectionHolder属性* 以及将newConnectionHolder属性设置为true,表示是一个新连接** 到这里,事务对象就已经获得了一个新连接*/txObject.setConnectionHolder(new ConnectionHolder(newCon), true);}//获取事务对象的连接持有者,将synchronizedWithTransaction设置为true,即资源标记为与事务同步。txObject.getConnectionHolder().setSynchronizedWithTransaction(true);//获取内部保存的连接con = txObject.getConnectionHolder().getConnection();/** 使用给定的事务语义准备给定的Connection,就是设置数据库事务的隔离级别,只读标志属性** 如果我们配置的隔离级别属性是ISOLATION_DEFAULT,即采用默认隔离级别,或者不是默认的隔离级别但是与连接的隔离级别一致,那么将返回null* 否则将返回从连接中直接获取的隔离级别(如果有)*/Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);//此前的隔离级别设置给事务对象的previousIsolationLevel属性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).//如有必要,切换为手动提交。//从Druid数据源中获取的连接DruidPooledConnection就是默认自动提交,即getAutoCommit返回trueif (con.getAutoCommit()) {txObject.setMustRestoreAutoCommit(true);if (logger.isDebugEnabled()) {logger.debug("Switching JDBC Connection [" + con + "] to manual commit");}/** 如果上面判断是自动提交,那么切换为手动提交,为什么呢?如果不手动提交,* 那么一个方法中执行多个sql语句时将会每执行一个提交一次,无法实现事务的控制* 开启手动提交就能实现方法级别的整体事务控制** 并且,开启手动提交时,将会自动开启事物*/con.setAutoCommit(false);}//事务已经开启,此后的sql语句,如果没有手动commit,那么将不会真正的提交给数据库//用户本次对数据库开始进行操作到用户执行commit命令之间的一系列操作为一个完整的事务周期。/*** 事务开始后立即准备事务连接,主要是对于只读事务的优化操作(需要手动开启)* 如果将"enforceReadOnly"标志设置为true(默认为false),并且事务定义指示只读事务,* 则默认实现将执行"SET TRANSACTION READ ONLY"这一个sql语句。* 请注意mysql只读事务不要开启,oracle的只读事务可以开启*/prepareTransactionalConnection(con, definition);//设置事务ConnectionHolder的transactionActive属性为true,表示激活当前连接的事务//此前判断是否有开启事务的isExistingTransaction方法就会判断这个属性txObject.getConnectionHolder().setTransactionActive(true);//设置实际超时时间int timeout = determineTimeout(definition);//如果不是默认超时时间-1,那么将if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {//那么设置超时时间,实际上就是根据设置的值和当前时间转换为未来的毫秒值并创建新Date配置给deadline属性//在其他数据库操作框架操作时将会获取该参数txObject.getConnectionHolder().setTimeoutInSeconds(timeout);}// Bind the connection holder to the thread.//如果是新的连接持有者,即newConnectionHolder属性为trueif (txObject.isNewConnectionHolder()) {//绑定ConnectionHolder资源到TransactionSynchronizationManager的resources属性中//key就是当前的属性源,value就是ConnectionHolderTransactionSynchronizationManager.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);}}
  • prepareConnectionForTransaction准备事务连接

使用给定的事务语义准备给定的Connection,就是将我们设置的隔离级别isolationLevel,只读标志readOnly属性赋给当前事务连接。如果我们配置的隔离级别属性是ISOLATION_DEFAULT,即采用默认隔离级别,或者不是默认的隔离级别但是与连接的隔离级别一致,那么将返回null,否则将设置连接的隔离级别为指定的级别,并且返回从连接中获取的隔离级别(如果有)。

/*** DataSourceUtils的方法* <p>* 使用给定的事务语义准备给定的Connection。** @param con        需要准备的连接* @param definition 适用的事务定义* @return 连接先前的隔离级别,可能为null*/
@Nullable
public static Integer prepareConnectionForTransaction(Connection con, @Nullable TransactionDefinition definition)throws SQLException {Assert.notNull(con, "No Connection specified");boolean debugEnabled = logger.isDebugEnabled();// 设置只读标志。if (definition != null && definition.isReadOnly()) {try {if (debugEnabled) {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);}}// 应用特定的隔离级别(如果有)。Integer previousIsolationLevel = null;//如果存在隔离级别并且不等于默认配置,即不等于ISOLATION_DEFAULT(该级别的意思是使用数据库的默认级别)if (definition != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {if (debugEnabled) {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());}}//返回此前的连接的隔离级别,可能为nullreturn previousIsolationLevel;
}
  • prepareTransactionalConnection优化只读事务
      事务开始后立即准备事务连接,主要是对于只读事务的优化操作(需要手动开启)。如果将事务管理器的"enforceReadOnly"标志设置为true(默认为false),并且事务定义指示只读事务,则默认实现将执行"SET TRANSACTION READ ONLY"这一个sql语句。
      "SET TRANSACTION READ ONLY"这个sql的意思就是告诉数据库,此事务中的后续sql语句将只有查询操作,不能进行DML操作。在"SET TRANSACTION READ ONLY"之后的查询语句将不会查询到该事物期间提交的内容,只能查询到事务开始之前提交的内容,相当于查询一个快照。进行只读事务设置之后,将有效减轻数据库压力。对于同一个表进行更新操作时,只读事务不会被阻塞,可以正常的执行查询操作,在只读事务操作期间也不会影响其他事务!
/*** DataSourceTransactionManager的方法* 如果将"enforceReadOnly"标志设置为true,并且事务定义指示只读事务,* 则默认实现将执行"SET TRANSACTION READ ONLY"sql语句。** @param con        当前连接* @param definition 为当前方法配置的事务定义*/
protected void prepareTransactionalConnection(Connection con, TransactionDefinition definition)throws SQLException {//如果将"enforceReadOnly"标志设置为true,并且事务定义指示只读事务if (isEnforceReadOnly() && definition.isReadOnly()) {//那么获取Statement,并且执行"SET TRANSACTION READ ONLY"sql语句try (Statement stmt = con.createStatement()) {stmt.executeUpdate("SET TRANSACTION READ ONLY");}}
}/*** DataSourceTransactionManager的属性* <p>* 表示是否通过对事务连接显式执行sql语句强制执行事务的只读性质,默认为false*/
private boolean enforceReadOnly = false;/*** 返回是否通过对事务连接显式执行sql语句强制执行事务的只读性质。*/
public boolean isEnforceReadOnly() {return this.enforceReadOnly;
}

上面说了这么多好处,很遗憾的是DataSourceTransactionManager的enforceReadOnly属性默认为false,并且大部分开发者也不知道这个优化,因此大多数情况下并不会执行该sql语句,即不会进行优化。如果要开启,那么可以这么设置:

/*** 配置DataSourceTransactionManager* 用于管理某一个数据库的事务*/
@Bean
public DataSourceTransactionManager transactionManager() {DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(druidDataSource());//设置只读事务优化dataSourceTransactionManager.setEnforceReadOnly(true);//传入一个数据源return dataSourceTransactionManager;
}

当然如果你真的这么开启了,并且你通过@Transactional注解设置了某个方法的事务的readOnly属性为true,那么确实会执行该方法,但是你讲将会收到一个异常:“Connection is read-only. Queries leading to data modification are not allowed”。原因是什么呢?很简单,在前面的prepareConnectionForTransaction方法中,连接被设置为只读,然而在随后的prepareTransactionalConnection方法中,执行该sql语句的却是executeUpdate方法,自然会抛出异常!所以说,这个优化还不能随便开。或者说,是因为不同的数据库对于Spring的readOnly属性的支持是不一样的,mysql支持Spring的readOnly参数,即支持JDBC的con.setReadOnly(true),因此就没必要再设置enforceReadOnly为true,而oracle则仅支持在Oracle server的设置而非JDBC驱动的配置,因此不支持con.setReadOnly(true),所以实际上Spring的readOnly配置对于Oracle无效,所以Oracle数据库可以开启此优化,mysql则不必要。

  • determineTimeout确定超时时间

确定给定事务定义的实际超时时间。如果事务定义未指定非默认值,则将使用默认超时。

/*** AbstractPlatformTransactionManager的方法* <p>* 确定给定事务定义的实际超时时间。* 如果事务定义未指定非默认值,则将使用默认超时。** @param definition 事务定义* @return 实际使用的超时时间*/
protected int determineTimeout(TransactionDefinition definition) {//如果不是默认超时时间,那么使用指定的事件if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {return definition.getTimeout();}//否则使用默认超时return getDefaultTimeout();
}/*** AbstractPlatformTransactionManager的属性* <p>* 默认超时时间(秒),默认值为-1,表示使用基础事务系统的默认超时;*/
private int defaultTimeout = TransactionDefinition.TIMEOUT_DEFAULT;public final int getDefaultTimeout() {return this.defaultTimeout;
}
  • setTimeoutInSeconds设置超时deadline

这里实际上就是根据设置的值和当前时间转换为未来的毫秒值并创建新Date配置给deadline属性,在其他数据库操作框架具体操作时将会获取并应用该参数。比如mybatis,在执行sql之前会获取到超时时间,计算之后会通过Statement.setQueryTimeout方法来设置,也就是说这个超时时间是执行sql之前的代码执行时间+sql执行时间,如果执行时间超过了设置时间就会抛出异常,这个异常就会被spring事务切面捕获到最终导致事务回滚,而如果在sql执行完毕之后的方法处理时间超过了这个超时时间,那么是不会进行回滚的,事务将会正常提交。

/*** ConnectionHolder的父类ResourceHolderSupport的方法* <p>* 设置此对象的超时(以秒为单位)。** @param seconds 到期前的秒数*/
public void setTimeoutInSeconds(int seconds) {setTimeoutInMillis(seconds * 1000L);
}/*** ConnectionHolder的父类ResourceHolderSupport的属性*/
@Nullable
private Date deadline;/*** 设置此对象的超时(以毫秒为单位)。** @param millis 到期前的毫秒数*/
public void setTimeoutInMillis(long millis) {//根据当前时间和超时时间计算出到期的Datethis.deadline = new Date(System.currentTimeMillis() + millis);
}
  • bindResource绑定资源到resources

对于新获取的连接资源会被绑定到TransactionSynchronizationManager的resources线程本地变量属性中(resources我们在此前就见过了)。key就是当前的属性源DataSource,value就是ConnectionHolder。

/*** 事务资源*/
private static final ThreadLocal<Map<Object, Object>> resources =new NamedThreadLocal<>("Transactional resources");/*** TransactionSynchronizationManager的方法* <p>* 将给定key的给定资源value绑定到当前线程。* 对于DataSourceTransactionManager,key就是DataSource实例,value就是ConnectionHolder** @param key   将值绑定到的键(通常是资源工厂,比如dataSource)* @param value 要绑定的值(通常是活动资源对象,比如数据库连接)* @throws IllegalStateException 如果已经有绑定到线程的值*/
public static void bindResource(Object key, Object value) throws IllegalStateException {Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);Assert.notNull(value, "Value must not be null");//获取当前线程的本地资源mapMap<Object, Object> map = resources.get();//如果找不到,则设置一个Mapif (map == null) {map = new HashMap<>();resources.set(map);}//将actualKey和value存入map中,返回旧的valueObject oldValue = map.put(actualKey, value);// Transparently suppress a ResourceHolder that was marked as void...if (oldValue instanceof ResourceHolder && ((ResourceHolder) oldValue).isVoid()) {oldValue = null;}//如果已经有绑定到线程的当前key的值,则抛出异常if (oldValue != null) {throw new IllegalStateException("Already value [" + oldValue + "] for key [" +actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");}if (logger.isTraceEnabled()) {logger.trace("Bound value [" + value + "] for key [" + actualKey + "] to thread [" +Thread.currentThread().getName() + "]");}
}
  • prepareSynchronization准备事务同步
      
    该方法通常用在doBegin开启新事物之后,用于准备事务同步,就是将当前事务的一系列属性绑定到TransactionSynchronizationManager的对应的线程本地变量中。
/*** AbstractPlatformTransactionManager的方法* <p>* 适当地初始化事务同步。*/
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {//是否是新同步,在真正的开启新事务的时候(比如第一次进入事务方法或者传播行为是REQUIRES_NEW),一般都是trueif (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();}
}private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =new NamedThreadLocal<>("Transaction synchronizations");/*** TransactionSynchronizationManager的方法* <p>* 激活当前线程的事务同步。由事务管理器在事务开始时调用。** @throws IllegalStateException 如果同步已处于活动状态*/
public static void initSynchronization() throws IllegalStateException {//如果同步已处于活动状态,即synchronizations保存的线程本地变量不为null,则抛出异常if (isSynchronizationActive()) {throw new IllegalStateException("Cannot activate transaction synchronization - already active");}logger.trace("Initializing transaction synchronization");//否则就为当前初始化一个线程本地变量,这是一个空的LinkedHashSet//虽然没有任何的TransactionSynchronization,但是已经不为null了synchronizations.set(new LinkedHashSet<>());
}
  • prepareTransactionStatus准备事务状态
  • prepareTransactionStatus方法和上面的startTransaction方法相比,其内部会调用newTransactionStatus和prepareSynchronization,但不会调用doBegin方法,因此不会真正的开启事物。返回的TransactionStatus,其内部保存了其他的资源,比如被挂起的事务信息。
  • 如果是第一次进入事务方法,即当前方法是最外层事务方法,并且传播行为是PROPAGATION_SUPPORTS或PROPAGATION_NEVER或PROPAGATION_NOT_SUPPORTED,那么会调用该方法,它的newTransaction参数为false,suspendedResources参数为null。即这些传播行为都不会真正的开启数据库级别的事务(不会获取新的连接)。
  • 如果是已存在外层事务,即当前方法是内层事务方法,并且传播行为是PROPAGATION_NOT_SUPPORTED或者PROPAGATION_NESTED或者PROPAGATION_SUPPORTS或者PROPAGATION_REQUIRED或者PROPAGATION_MANDATORY,那么也有可能调用这个方法,它的newTransaction参数为false,suspendedResources参数为被挂起的外层事务资源。即这些传播行为都不会真正的开启数据库级别的事务(不会获取新的连接,对于普通事物来说)。
/**1. 根据给定参数创建一个新的TransactionStatus,并在适当时初始化事务同步,不会真正开启新事物。2.  3. @param definition         为当前方法设置的事务定义4. @param transaction        当前已存在的事务5. @param newTransaction     是否是新事物,如果是外层事务方法,则为true,如果是内层方法则为false6. @param newSynchronization 是否是新事务同步7. @param debug              是否支持debug日志8. @param suspendedResources 被挂起的资源9. @return 一个TransactionStatus的实现*/
protected final DefaultTransactionStatus prepareTransactionStatus(TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {//通过newTransactionStatus创建一个DefaultTransactionStatusDefaultTransactionStatus status = newTransactionStatus(definition, transaction, newTransaction, newSynchronization, debug, suspendedResources);//中间缺少了doBegin真正开启事务的访法,所以仅仅是创建了一个简单的TransactionStatus//包存了一些其他信息,比如被挂起的资源信息//准备事务同步prepareSynchronization(status, definition);return status;

}

  • 如果当前已存在事务,则handleExistingTransaction处理已存在事务
      
    如果进入事务切面之后,获取到了已存在的连接并且开启了事务(通过isExistingTransaction方法判断),那么将会执行handleExistingTransaction方法执行已存在事务时的逻辑,并返回一个TransactionStatus。该方法同样将会根据此事务切面设置的事务传播行为走不同的执行流程,比如加入当前事务、新建事务、抛出异常等等逻辑。
      大概逻辑是:
  1. 如果当前配置的传播行为是PROPAGATION_NEVER,该行为的特点是:当前方法一定以非事务的方式运行,并且如果当前存在事务,则直接抛出异常,所以这里由于存在外部事务,那么直接抛出异常:“Existing
    transaction found for transaction marked with propagation ‘never’”。
  2. 如果当前配置的传播行为是PROPAGATION_NOT_SUPPORTED,该行为的特点是:当前方法一定以非事务的方式运行,如果当前存在事务,则把当前事务挂起,直到当前方法执行完毕,才恢复外层事务。
    • 首先调用suspend方法挂起外层事务,返回被挂起的资源。由于存在外层事务,所以这里的参数就是获取的外层事务参数。
    • 随后调用prepareTransactionStatus方法返回一个新的TransactionStatus,并在适当时初始化事务同步。同样,该方法和上面的startTransaction方法相比,其内部会调用newTransactionStatus和prepareSynchronization,但不会调用doBegin方法,因此不会真正的开启事物。这里它的newTransaction参数为false,transaction参数为null,suspendedResources参数为被挂起的外层事务资源。
  1. 如果当前配置的传播行为是PROPAGATION_REQUIRES_NEW,该行为的特点是:当前方法开启一个新事物独立运行,从不参与外部的现有事务。则当内部事务开始执行时,外部事务(如果存在)将被挂起,内务事务结束时,外部事务将继续执行。
    • 首先调用suspend方法挂起外层事务,返回被挂起的资源。由于存在外层事务,所以这里的参数就是获取的外层事务参数。
    • 随后调用startTransaction方法真正的开启一个数据库级别的事务(将会获取新的连接开启一个新事物,旧的连接和事务则在上面的suspend方法中被挂起保存)。
  1. 如果当前配置的传播行为是PROPAGATION_NESTED,该行为的特点是:
    如果当前存在事务,则创建一个新“事务”作为当前事务的嵌套事务来运行;如果当前没有事务,则等价于PROPAGATION_REQUIRED,即会新建一个事务运行。
    • 调用isNestedTransactionAllowed方法判断是否允许PROPAGATION_NESTED行为,默认不允许,但是DataSourceTransactionManager重写为允许。不允许就抛出异常:“Transaction manager does not allow nested transactions……”。
    • 调用useSavepointForNestedTransaction方法判断是否对“嵌套事务”使用保存点Savepoint来实现:
    • 如果允许,那么首先调用prepareTransactionStatus方法返回一个新的TransactionStatus,并在适当时初始化事务同步。这里它的newTransaction参数为false,transaction参数为外层事务,suspendedResources参数为null,newSynchronization参数为false。随后调用createAndHoldSavepoint创建保存点,将使用数据库的保存点的特性来实现“嵌套事务”,这是用语大部分普通事务。
    • 否则将调用startTransaction方法通过在外层事务中嵌套的begin和commit/rollback调用来开启真正的嵌套事务,不过通常仅用于JTA:如果存在预先存在的JTA事务,则可以在此处激活Spring同步。
  1. 剩下的传播行为就是PROPAGATION_SUPPORTS或者PROPAGATION_REQUIRED或者PROPAGATION_MANDATORY,如果是这些行为,那么它们的一个共同的特性就是:参与到当前事务中去,也是默认传播行为的逻辑。

    • 那么这里同样调用prepareTransactionStatus方法,这里它的newTransaction参数为false,transaction参数为外层事务,suspendedResources参数为null。

从源码中我们能够看到,如果传播行为是PROPAGATION_NESTED

  1. 对于普通事务(比如DataSourceTransactionManager处理的事务),它的“嵌套事务”是通过SavePoint保存点来实现的,实际上就是同一个事务。基于保存点的特性,此时,“内层事务”依赖“外层事物”:层事务操作失败时只是自身回到到保存点的位置,不会引起外层事务的回滚,而外层事务因失败而回滚时,内层事务所做的所有动作也会回滚。在提交时,在外层事务提交之后内层事务才能提交,仅需要提交外层事务即可。由于实际上只有一个物理事务,那么内层事务会继承外层外层事务的隔离级别和超时设置等属性。
  2. 对于JTA事务(分布式事务),我们能看到是调用了startTransaction方法,因此将会在原本的事务中通过嵌套的begin和commit/rollback调用来开启的嵌套事务,这是真正的嵌套事务。
/*** AbstractPlatformTransactionManager的方法* <p>* 根据现有事务创建一个TransactionStatus,处理事务的传播行为** @param definition   当前事务定义* @param transaction  事物对象,内部包含此前的事务信息* @param debugEnabled 日志级别支持* @return 事务状态对象*/
private TransactionStatus handleExistingTransaction(TransactionDefinition definition, Object transaction, boolean debugEnabled)throws TransactionException {/** 1 如果当前配置的传播行为是PROPAGATION_NEVER,该行为的特点是:* 当前方法一定以非事务的方式运行,并且如果当前存在事务,则直接抛出异常* 所以这里直接抛出异常*/if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {throw new IllegalTransactionStateException("Existing transaction found for transaction marked with propagation 'never'");}/** 2 如果当前配置的传播行为是PROPAGATION_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);//为给定参数创建一个新的TransactionStatus,并在适当时初始化事务同步。//这里的第二个参数事务属性为null,表示当前方法以非事务的方式执行return prepareTransactionStatus(definition, null, false, newSynchronization, debugEnabled, suspendedResources);}/** 3 如果当前配置的传播行为是PROPAGATION_REQUIRES_NEW,该行为的特点是:* 当前方法开启一个新事物独立运行,从不参与外部的现有事务。则当内部事务开始执行时,* 外部事务(如果存在)将被挂起,内务事务结束时,外部事务将继续执行。*/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 {//开启一个新事务并返回return startTransaction(definition, transaction, debugEnabled, suspendedResources);} catch (RuntimeException | Error beginEx) {//恢复被挂起的事务resumeAfterBeginException(transaction, suspendedResources, beginEx);throw beginEx;}}/** 4 如果当前配置的传播行为是PROPAGATION_NESTED,该行为的特点是:* 如果当前存在事务,则创建一个新“事务”作为当前事务的嵌套事务来运行;* 如果当前没有事务,则等价于PROPAGATION_REQUIRED,即会新建一个事务运行。**/if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {//判断是否允许PROPAGATION_NESTED行为,默认不允许,但是DataSourceTransactionManager重写为为允许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() + "]");}//返回是否对嵌套事务使用保存点,默认true,JtaTransactionManager设置为false//PROPAGATION_NESTED就是通过Savepoint保存点来实现的if (useSavepointForNestedTransaction()) {//并没有挂起当前事务,创建TransactionStatus,transaction参数就是当前事务DefaultTransactionStatus status =prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);/** 通过TransactionStatus实现的SavepointManager API在现有的Spring管理的事务中创建保存点,通常使用JDBC 3.0保存点。*/status.createAndHoldSavepoint();return status;} else {// 通过在事务中嵌套的begin和commit / rollback调用开启的嵌套事务。// 通常仅用于JTA:如果存在预先存在的JTA事务,则可以在此处激活Spring同步。return startTransaction(definition, transaction, debugEnabled, null);}}//剩下的传播行为就是PROPAGATION_SUPPORTS或者PROPAGATION_REQUIRED或者PROPAGATION_MANDATORY。if (debugEnabled) {logger.debug("Participating in existing transaction");}//是否在参与现有事务之前进行验证,默认falseif (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");}}}//并没有挂起当前事务,而是直接参与到当前事务中去,transaction参数就是当前的事务boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}
  • createAndHoldSavepoint创建保存点

该方法很简单,最终会调用当前的JDBC连接Connection的setSavepoint方法创建一个保存点,并且被设置给当前TransactionStatus对象的savepoint属性。

/*** AbstractTransactionStatus的方法* <p>* 创建一个保存点并将其保存在事务中。** @throws NestedTransactionNotSupportedException 如果基础事务不支持保存点*/
public void createAndHoldSavepoint() throws TransactionException {setSavepoint(getSavepointManager().createSavepoint());
}
/*** AbstractTransactionStatus的属性*/
@Nullable
private Object savepoint;/*** AbstractTransactionStatus的方法* <p>* 设置此事务的保存点,对PROPAGATION_NESTED有用。*/
protected void setSavepoint(@Nullable Object savepoint) {this.savepoint = savepoint;
}

getSavepointManager获取获取保存点管理器,实际上创建的内部事务对象都是SavepointManager接口的实现,具有获取保存点的方法!因此返回的实际上就是内部的事务对象,对于DataSourceTransactionManager来说创建的内部事务就是DataSourceTransactionObject。

/*** DefaultTransactionStatus的属性* <p>* 获取基础事务对象的SavepointManager,实际上就是获取的内部事务对象*/
@Nullable
private final Object transaction;/*** DefaultTransactionStatus的方法* <p>* 获取基础事务对象的SavepointManager,实际上就是获取的内部事务对象*/
@Override
protected SavepointManager getSavepointManager() {//获取内部事务,对于DataSourceTransactionManager来说创建的内部事务就是DataSourceTransactionObjectObject transaction = this.transaction;if (!(transaction instanceof SavepointManager)) {throw new NestedTransactionNotSupportedException("Transaction object [" + this.transaction + "] does not support savepoints");}return (SavepointManager) transaction;
}

createSavepoint用于从当前保存点管理器(事务对象)中创建一个保存点,实际上就是获取事务对象里面的ConnectionHolder,然后在获取ConnectionHolder里面的Connection,最后调用Connection.setSavepoint方法创建并获取保存点。

/*** DataSourceTransactionObject的父类JdbcTransactionObjectSupport的方法* <p>* 创建一个JDBC 3.0保存点并返回它。*/
@Override
public Object createSavepoint() throws TransactionException {//校验规则并获取,此前创建的ConnectionHolder,其内部保存了获取的连接ConnectionConnectionHolder conHolder = getConnectionHolderForSavepoint();try {//如果不允许保存点,那么抛出异常,默认允许if (!conHolder.supportsSavepoints()) {throw new NestedTransactionNotSupportedException("Cannot create a nested transaction because savepoints are not supported by your JDBC driver");}//如果被设置为仅回滚,那么抛出异常if (conHolder.isRollbackOnly()) {throw new CannotCreateTransactionException("Cannot create savepoint for transaction which is already marked as rollback-only");}return conHolder.createSavepoint();} catch (SQLException ex) {throw new CannotCreateTransactionException("Could not create JDBC savepoint", ex);}
}/*** DataSourceTransactionObject的父类JdbcTransactionObjectSupport的方法* <p>* 为了创建一个JDBC 3.0保存而获取ConnectionHolder。*/
protected ConnectionHolder getConnectionHolderForSavepoint() throws TransactionException {//如果不允许保存点,那么抛出异常,默认允许if (!isSavepointAllowed()) {throw new NestedTransactionNotSupportedException("Transaction manager does not allow nested transactions");}//如果没有ConnectionHolder,那么抛出异常,默认允许if (!hasConnectionHolder()) {throw new TransactionUsageException("Cannot create nested transaction when not exposing a JDBC transaction");}//返回此前创建的ConnectionHolder,其内部保存了获取的连接return getConnectionHolder();
}//ConnectionHolder的方法的属性/*** 保存点名称的前缀。*/
public static final String SAVEPOINT_NAME_PREFIX = "SAVEPOINT_";/*** 从此连接中获取的保存点的数量*/
private int savepointCounter = 0;/*** ConnectionHolder的方法* 为当前连接创建一个新的JDBC 3.0保存点,只用SAVEPOINT_+savepointCounter作为保存点的名称** @return 新的保存点* @throws SQLException if thrown by the JDBC driver*/
public Savepoint createSavepoint() throws SQLException {//获取数量自增1this.savepointCounter++;//获取内部的JDBC连接,并通过连接设置一个保存点,返回创建的Savepointreturn getConnection().setSavepoint(SAVEPOINT_NAME_PREFIX + this.savepointCounter);
}

四、小结

  1. createTransactionIfNecessary方法创建并返回一个TransactionInfo对象,并且在此过程中,将会调用getTransaction方法获取事务TransactionStatus。

  2. getTransaction方法就是Spring事务处理的核心方法之一,该方法根据配置的各种事务传播行为以及是否存在外层事务做出不同的处理,方法执行完毕将可能开启了新事物,也可能没有开启,甚至抛出异常。

  3. TransactionInfo内部保存了事务管理器transactionManager、事务属性transactionAttribute、全路径方法名joinpointIdentification。还保存了当前方法的事务transactionStatus,以及前一个方法的事务信息对象oldTransactionInfo。

  4. TransactionStatus实际类型为DefaultTransactionStatus,它持有一个transaction内部事务对象、被挂起的事务资源以及一些事务的属性,这个内部事务对象由事务管理器的实现各自创建,不同的事务管理器将会创建不同的类型,因此使用Object来表示,对于DataSourceTransactionManager来说,它创建的事务对象就是DataSourceTransactionObject。

  5. DataSourceTransactionObject内部持有一个ConnectionHolder对象以及一些事务的属性,ConnectionHolder对象内部持有一个为了配置数据库事务而获取的JDBC连接Connection,以及是否开启了事务的标志transactionActive,以及其他属性,比如保存点计数。

  6. 最终,我们为某个方法定义的事务属性,除了传播行为之外(Spring提供的特性并自行处理),都会反应到Connection的对应操作上,比如隔离级别、超时时间,是否只读等等,关键方法就是doBegin,该方法用于真正的开启数据库层面的事务。

  7. 我们用了很长的文章讲解了createTransactionIfNecessary方法的逻辑和源码,这是Spring 事务开启的核心处理方法(可能并未真正的开启事务),剩下的方法比如proceedWithInvocation、completeTransactionAfterThrowing、cleanupTransactionInfo、commitTransactionAfterReturning就是在开启事物之后的处理方法,比如回滚、提交、恢复事务等等。

参考文章

Spring TX源码分析相关推荐

  1. Spring Cloud源码分析(二)Ribbon(续)

    因文章长度限制,故分为两篇.上一篇:<Spring Cloud源码分析(二)Ribbon> 负载均衡策略 通过上一篇对Ribbon的源码解读,我们已经对Ribbon实现的负载均衡器以及其中 ...

  2. Spring AOP 源码分析 - 拦截器链的执行过程

    1.简介 本篇文章是 AOP 源码分析系列文章的最后一篇文章,在前面的两篇文章中,我分别介绍了 Spring AOP 是如何为目标 bean 筛选合适的通知器,以及如何创建代理对象的过程.现在我们的得 ...

  3. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

  4. Spring AOP 源码分析 - 筛选合适的通知器

    1.简介 从本篇文章开始,我将会对 Spring AOP 部分的源码进行分析.本文是 Spring AOP 源码分析系列文章的第二篇,本文主要分析 Spring AOP 是如何为目标 bean 筛选出 ...

  5. spring AOP源码分析(一)

    spring AOP源码分析(一) 对于springAOP的源码分析,我打算分三部分来讲解:1.配置文件的解析,解析为BeanDefination和其他信息然后注册到BeanFactory中:2.为目 ...

  6. 一步一步手绘Spring AOP运行时序图(Spring AOP 源码分析)

    相关内容: 架构师系列内容:架构师学习笔记(持续更新) 一步一步手绘Spring IOC运行时序图一(Spring 核心容器 IOC初始化过程) 一步一步手绘Spring IOC运行时序图二(基于XM ...

  7. 精尽Spring Boot源码分析 - 内嵌Tomcat容器的实现

    概述 我们知道 Spring Boot 能够创建独立的 Spring 应用,内部嵌入 Tomcat 容器(Jetty.Undertow),让我们的 jar 无需放入 Servlet 容器就能直接运行. ...

  8. 【Spring】Spring AOP源码分析-导读(一)

    文章目录 1.简介 2.AOP 原理 3.AOP 术语及相应的实现 3.1 连接点 - Joinpoint 3.2 切点 - Pointcut 3.3 通知 - Advice 3.4 切面 - Asp ...

  9. 视频教程-Spring底层源码分析-Java

    Spring底层源码分析 鲁班学院-子路老师曾就职于谷歌.天猫电商等多家互联网公司,历任java架构师.研发经理等职位,参与并主导千万级并发电商网站与后端供应链研发体系搭建,多次参与电商大促活动技术保 ...

最新文章

  1. 关于l ibrary not found for -lz.1.2.3 编译错误
  2. 高级数据结构与算法 | 深度遍历搜索(DFS)与广度遍历搜索(BFS)
  3. 使用PerfView监测.NET程序性能(四):折叠,过滤和时间范围选择
  4. uni-app微信小程序uni.navigateTo跳转无效问题;记录一次uni-app页面跳转无效,来回跳转问题;wx.navigateTo ,跳转超过10次怎么点不动的解决办法。
  5. STL vector 用法
  6. java版mosquitto客户端使用SSL功能的具体操作总结
  7. 360安全桌面 v2.7.0.1060 官方版
  8. linux恢复,Linux 5.13还原及修复明尼苏达大学的问题补丁
  9. 什么?四六级及格线不是425分?!
  10. 解决 Chrome 使用高德地图卡顿
  11. 计算机十六进制是什么意思,2进制和16进制是什么意思?它们之间如何换算?
  12. WIN10 + Ubuntu18.04 双系统安装教程(新机)
  13. Java实现 谁不爱打牌
  14. Bili视频弹幕播放器后台源码
  15. 聚类方法:DBSCAN算法研究(1)--DBSCAN原理、流程、参数设置、优缺点以及算法
  16. 苹果手机怎么清理喇叭灰尘_手机喇叭声音小怎么办?打电话听不见怎样清灰?...
  17. S5PV210-uboot源码分析-第二阶段
  18. 在职计算机技术考研英语自我介绍,2019考研复试英语自我介绍范文--在职考研
  19. Failed to shutdown DBConsole Gracefully
  20. 基于Python的股市市场情绪分析

热门文章

  1. /bin/sh -c的必要性
  2. Codeforces 1015F Bracket Substring AC自动机 + dp
  3. WPF中的Bitmap与byte
  4. __block 和 __weak的区别
  5. Linux第六周学习总结——进程额管理和进程的创建
  6. Python学习笔记:错误,测试,调试(承)
  7. 深度学习入门初步——MNIST数据格式如何使用
  8. B树与B+树 两者的区别
  9. 云炬60s看世界20211205
  10. 一、“用黑色的眼睛寻找光明”