先说一下启动过程中的几个点:

  加载配置文件:

  AbstractAutowireCapableBeanFactory.doCreateBean --> initializeBean --> applyBeanPostProcessorsAfterInitialization --> beanProcessor.postProcessAfterInitialization --> AbstractAutoProxyCreator.postProcessAfterInitialization --> wrapIfNecessary(bean, beanName, cacheKey) --> getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null)Advisor是Pointcut和Advice的配置器,它包括Pointcut和Advice,是将Advice注入程序中Pointcut位置的代码;AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary:调用txAdvice上图的事务通知设置数据源 DataSourceTransactionManager,ChooseDataSource是项目中配置的自定义的继承至AbstractRoutingDataSource的默认数据源,命名什么的:

  启动结束后,发起事务调用,首先拦截方法起CglibAopProxy.intercept --> ReflectiveMethodInvocation.proceed --> ExposeInvocationInterceptor.invoke --> TransactionInterceptor.invoke:

    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, new InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
});
}

  TransactionAspectSupport.invokeWithinTransaction :

  determineTransactionManager方法先判断当前事务有无配置特定事务管理器,如果没有判断是否设置过默认的事务管理器,都没有的情况下:

    public PlatformTransactionManager getTransactionManager() {
return this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
}

  接下来判断加载的事务属性是否存在或者当前事务管理器是否不是CallbackPreferringPlatformTransactionManager,符合条件会执行到createTransactionIfNecessary,先是:

        if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}

  DelegatingTransactionAttribute本身除了被实现后使用没有其他作用。然后从事务管理器中取出事务,doGetTransaction从之前set的数据源取出连接set给DataSourceTransactionObject:

    protected Object doGetTransaction() {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
txObject.setConnectionHolder(conHolder, false);
return txObject;
}

  然后判断事务是否已存在,如果存在例如嵌套事务,会根据定义的传播方式进行处理,具体处理后面会说,这里是还不存在。然后验证了一下事务是否超时。由于从事务定义(TransactionDefinition持有隔离级别等事务属性的对象)中取出的事务传播方式我这里是默认的 PROPAGATION_REQUIRED,第一句用于挂起事务的什么也没做,然后或许事务同步为激活同步,接着就到启动事务了DataSourceTransactionManager doBegin:

            if (txObject.getConnectionHolder() == null ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = this.dataSource.getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}

  先从数据源中getConnection,set给DataSourceTransactionObject,设置为同步事务,prepareConnectionForTransaction根据配置确定事务是否只读,被嵌套的事务中前一个的隔离级别txObject.setPreviousIsolationLevel(previousIsolationLevel):

  判断是否已设置为自动提交,如果是设置则设置事务对象为自动还原为自动提交,有点拗口,意思大概是当事务复用数据库连接时第一个事务提交后,同一个连接的下一个事务还是设置为自动提交,否则当前事务如果被设为手痛提交,因为连接池中的连接会被复用,在同一个连接上的后续事务可能需要手动调用conn.commit才能提交下一个事务,设置connection holder代表的连接的事务是活动的;检查超时;判断当前事务的连接是否是新创建的,是则注册给TransactionSynchronizationManager,通过ThreadLocal将线程和事务绑定;prepareSynchronization设置这个事务同步管理器是否包含实际执行的事务,当前线程事务隔离级别、是否只读以及事务定义名,初始化同步事务(private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal<Set<TransactionSynchronization>>("Transaction synchronizations")):

    public static void initSynchronization() throws IllegalStateException {
if (isSynchronizationActive()) {
throw new IllegalStateException("Cannot activate transaction synchronization - already active");
}
logger.trace("Initializing transaction synchronization");
synchronizations.set(new LinkedHashSet<TransactionSynchronization>());
}

  回到TransactionAspectSupport,prepareTransactionInfo方法创建事务信息,并通过ThreadLocal绑定给当前线程:

        TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
        private void bindToThread() {
// Expose current TransactionStatus, preserving any existing TransactionStatus
// for restoration after this transaction is complete.
this.oldTransactionInfo = transactionInfoHolder.get();
transactionInfoHolder.set(this);
}

  绑定中保留了当前事务状态,应该用来在嵌套事务中内层事务完成后恢复外层事务的现场。

  createTransactionIfNecessary结束,回到TransactionAspectSupport的invokeWithinTransaction方法,接下来是invocation.proceedWithInvocation,这个方法上面贴了,new InvocationCallback时实现了这个方法,代码只有一句调用了MethodInvocation的proceed,也就是环绕通知调用被切方法的方法(也有可能调用其他Interceptor的切面方法),我这直接调用了被切的方法,然而并没有直接走到后面的completeTransactionAfterThrowing或commitTransactionAfterReturning,也没有到清理事务信息的方法,因为有嵌套事务,于是被嵌套的切了,与上面过程相同之处就不说了,说说不同的。

  getTransaction这次因为已经存在事务,使用同一个连接,JdbcTransactionObjectSupport实例保存了connectionHolder,所以这次走进了这个判断分支:

        if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(definition, transaction, debugEnabled);
}

  handleExistingTransaction方法内根据不同的事务传播方式走不同的代码分支,我这就是默认REQUIRED使用现有事务,所以这个方法基本和没走也差不多。嵌套的都完了就走到cleanupTransactionInfo方法了,这方法实际调用了TransactionInfo.restoreThreadLocalStatus,实际上还原了之前的事务信息:

        private void restoreThreadLocalStatus() {
// Use stack to restore old transaction TransactionInfo.
// Will be null if none was set.
transactionInfoHolder.set(this.oldTransactionInfo);
}

  然后是commitTransactionAfterReturning:

    /**
* Execute after successful completion of call, but not after an exception was handled.
* Do nothing if we didn't create a transaction.
* @param txInfo information about the current transaction
*/
protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
if (txInfo != null && txInfo.hasTransaction()) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}

  commit方法在AbstractPlatformTransactionManager中,先提交的是内层的事务,这里需要提的,嵌套事务的子事务报错但没有抛给外层事务,可能会出现rollback-only的问题,defStatus.isLocalRollbackOnly()就是判断是否有内层事务出错设置rollbackOnly为true了,另外,关于全局事务,似乎说的是用的两段式XA?,不过目前用不上,只是一个连接对数据库,这里可以考虑下。该到具体处理提交的方法processCommit了。同样在AbstractPlatformTransactionManager中。prepareForCommit方法是空的,protected应该是准备给子类重写的,或者这就是我要找的。savepoint没有,由于是内层事务,triggerBeforeCommit、triggerBeforeCompletion、triggerAfterCommit和triggerAfterCommit方法没有执行,设置事务状态后,这个内层事务就提交完了。

  ExposeInvocationInterceptor(可以暴露出拦截器链,一般用不到它,用到时应该在链首)还原外层被拦截方法的执行:

    @Override
public Object invoke(MethodInvocation mi) throws Throwable {
MethodInvocation oldInvocation = invocation.get();
invocation.set(mi);
try {
return mi.proceed();
}
finally {
invocation.set(oldInvocation);
}
}

  这个外层就是实际被拦截的方法,会通过CglibAopProxy执行。

  再来就是提交外层事务了,cleanupTransactionInfo的old这次是null了,一样的流程就不说了,由于外层事务是创建了同步对象所以triggerBeforeCommit执行了:

TransactionSynchronizationUtils.triggerBeforeCommit(status.isReadOnly());
    public static void triggerBeforeCommit(boolean readOnly) {
for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
synchronization.beforeCommit(readOnly);
}
}

  这里的beforeCommit由于我用的是mybatis,所以如果执行的话,会执行到SqlSessionUtils.beforeCommit,然而由于SqlSessionUtils是不能被继承的,所以这里不太好动手脚,只能在这个类的外层想办法。triggerBeforeCompletion是类似的。外层事务会有获取status.isGlobalRollbackOnly()用于doCommit(status)之后是否报错,注意,就是说其实并不会打断提交的执行。doCommit(status):

    @Override
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Committing JDBC transaction on Connection [" + con + "]");
}
try {
con.commit();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
}

  没错,我们用的是druid,这里不需要解释了。之后就是一些完成回调,各种解绑、clear、reset了,之前设置的必须还原为自动提交会在doCleanupAfterCompletion还原,最后关闭连接。

  过程就是这样了,其实我想实验个想法,于是看了这些代码。以上。

org/springframework/transaction/interceptor/TransactionAspectSupport --> createTransactionIfNecessary -->

/org/springframework/transaction/support/AbstractPlatformTransactionManager --> status = tm.getTransaction(txAttr) --> doBegin

createTransactionIfNecessary --> {doCleanupAfterCompletion commitTransactionAfterReturning}

--> org/springframework/transaction/support/AbstractPlatformTransactionManager

--> commit --> processCommit --> {prepareForCommit, doCommit, triggerAfterCommit triggerAfterCompletion cleanupAfterCompletion}

doCommit --> DataSourceTransactionManager

cleanupAfterCompletion --> DataSourceTransactionManager --> doCleanupAfterCompletion

==========================================================

咱最近用的github:https://github.com/saaavsaaa

微信公众号:

                      

转载请注明出处

Spring事务执行过程的更多相关文章

  1. 调试:Spring AOP执行过程

    调试项目:https://github.com/1367356/laboratoryWeb 断点位置 点击查询:http://localhost:9002/queryNews?htmlid=15318 ...

  2. 深入理解 Spring 事务:入门、使用、原理

    大家好,我是树哥. Spring 事务是复杂一致性业务必备的知识点,掌握好 Spring 事务可以让我们写出更好地代码.这篇文章我们将介绍 Spring 事务的诞生背景,从而让我们可以更清晰地了解 S ...

  3. (4.1)Spring MVC执行原理和基于Java的配置过程

    一.Spring MVC执行原理和基于Java配置的配置过程 (一)Spring MVC执行过程,大致为7步. 所有的请求都会经过Spring的一个单例的DispacherServlet. Dispa ...

  4. Spring MVC执行原理和基于Java的配置过程

    一.Spring MVC执行原理和基于Java配置的配置过程 (一)Spring MVC执行过程,大致为7步. 所有的请求都会经过Spring的一个单例的DispacherServlet. Dispa ...

  5. spring事务详解(一)初探事务

    系列目录 spring事务详解(一)初探事务 spring事务详解(二)简单样例 spring事务详解(三)源码详解 spring事务详解(四)测试验证 spring事务详解(五)总结提高 引子 很多 ...

  6. spring事务详解(一)初探讨

    一.什么是事务 维基百科:数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成.理解:事务(Transaction)是数据库区别于文件系统的重要特性之一.传 ...

  7. Spring事务用法示例与实现原理

    关于Java中的事务,简单来说,就是为了保证数据完整性而存在的一种工具,其主要有四大特性:原子性,一致性,隔离性和持久性.对于Spring事务,其最终还是在数据库层面实现的,而Spring只是以一种比 ...

  8. 2018.11.12 Spring事务的实现和原理

    Spring事务用法示例与实现原理 关于事务,简单来说,就是为了保证数据完整性而存在的一种工具,其主要有四大特性:原子性.一致性.隔离性和持久性.对于Spring事务,其最终还是在数据库层面实现的,而 ...

  9. Spring系列(六):Spring事务源码解析

    一.事务概述 1.1 什么是事务 事务是一组原子性的SQL查询,或者说是一个独立的工作单元.要么全部执行,要么全部不执行. 1.2 事务的特性(ACID) ①原子性(atomicity) 一个事务必须 ...

随机推荐

  1. C#:查询某年(1900-2100)某月的日历

    using System;using System.Collections.Generic;public class Program    {     /********************主函数 ...

  2. 1675: [Usaco2005 Feb]Rigging the Bovine Election 竞选划区(题解第一弹)

    1675: [Usaco2005 Feb]Rigging the Bovine Election 竞选划区 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit:  ...

  3. 算法模板——平衡树Treap

    实现功能如下——1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3. 查询x数的排名(若有多个相同的数,因输出最小的排名)4. 查询排名为x的数5. 求x的前驱(前驱定义为小于x,且最大 ...

  4. webStorm Linux Ubuntu 中文搜狗输入问题

    1 打开安装路径下bin/webstorm.sh vim ~/WebStorm-145.597.6/bin/webstorm.sh 2.在打开文件的最前面加入如下代码: export XMODIFIE ...

  5. 利用Socket与硬件通信(智能家居)

    前几天做一个智能家居APP,硬件段使用的是ESP8266WIFI模块,其实不管是WIFI模块还是蓝牙,通信都是同样一个道理,获取IP和端口来进行通信. 我是通过XCOM v2.0 发送信息,移动端接收 ...

  6. linux 部署jenkins

    1.安装jdk,配置jdk路径,python路径 (当前用户的配置文件)  vi .bash_profile export JAVA_HOME=$HOME/local/jdk1.8.0_111 exp ...

  7. mac下安装nginx问题解决

    需要在mac上安装nginx,按照下面的博客链接一步步安装,但是碰到了些问题.下面写一下我的解决方式. (http://stevendu.iteye.com/blog/1535466) 1. 安装PC ...

  8. spring项目中dubbo相关的配置文件出现红叉的问题

    近来在eclipse中导入了一个web项目,但是发现项目上有红色的叉号. 原来是spring中关于dubbo的配置文件报错了. Multiple annotations found at this l ...

  9. (转)crontab安装(command not found)

    1. 确认crontab是否安装:执行 crontab -l 命令如果报 command not found,就表明没有安装 2. 安装 crontab执行 yum install -y vixie- ...

  10. dotweb——go语言的一个微型web框架(一)

    dotweb是16年正式托管到github的一个开源项目,go语言的web框架目前也有很多,出名的有bee和echo.它们都是很优秀的框架,但是我们喜欢更轻.更小的东西,经历一些之后我们更青睐微服务这 ...