温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Spring事务执行过程是怎样的

发布时间:2022-01-10 09:44:29 来源:亿速云 阅读:99 作者:柒染 栏目:网络安全

Spring事务执行过程是怎样的,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

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

  加载配置文件:Spring事务执行过程是怎样的

 Spring事务执行过程是怎样的

  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的默认数据源,命名什么的:

Spring事务执行过程是怎样的

  启动结束后,发起事务调用,首先拦截方法起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 :

 Spring事务执行过程是怎样的

Spring事务执行过程是怎样的

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

Spring事务执行过程是怎样的

    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):

Spring事务执行过程是怎样的

  判断是否已设置为自动提交,如果是设置则设置事务对象为自动还原为自动提交,有点拗口,意思大概是当事务复用数据库连接时第一个事务提交后,同一个连接的下一个事务还是设置为自动提交,否则当前事务如果被设为手痛提交,因为连接池中的连接会被复用,在同一个连接上的后续事务可能需要手动调用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);
        }

 Spring事务执行过程是怎样的

 Spring事务执行过程是怎样的

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

  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);
        }
    }

Spring事务执行过程是怎样的

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

关于Spring事务执行过程是怎样的问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注亿速云行业资讯频道了解更多相关知识。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI