44--Spring @Transactional聲明式事物(一)事物管理步驟簡析

1.引

上一節(jié)已經(jīng)分析了基于@Transactional注解的聲明式事物事物標簽提取,接下來我們就接下來我們就要分析Spring事物的實現(xiàn)過程了,因為這里會涉及到事物傳播特性、事物嵌套調(diào)用等等很多復(fù)雜的內(nèi)容,所以第一個分析,還是以最簡單的形式去分析,及單個service下的單個方法調(diào)用。業(yè)務(wù)方法摘要如下,在業(yè)務(wù)方法上配置了Transactional注解,并將事物傳播特性定義為REQUIRED。在分析完這個最簡單的例子之后,再去分析比較復(fù)雜的事物調(diào)用過程。。。

@Override
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void delete() throws RuntimeException {
    System.out.println("==調(diào)用AccountService的delete方法\n");
    jdbcTemplate.update(insert_sql);

    throw new RuntimeException("==AccountService的delete方法手動拋出異常");
}
2.攔截器鏈調(diào)用回顧

攔截器鏈調(diào)用入口:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    MethodInvocation invocation;
    Object oldProxy = null;
    boolean setProxyContext = false;

    TargetSource targetSource = this.advised.targetSource;
    Object target = null;

    try {
        // 1、處理equals方法,如果接口中沒有定義equals而在實現(xiàn)類中覆蓋了equals方法,那么該equals方法不會被增強
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // The target does not implement the equals(Object) method itself.
            return equals(args[0]);
        }
        // 2、處理hashCode方法,如果接口中沒有定義hashCode而在實現(xiàn)類中覆蓋了hashCode方法,那么該hashCode方法不會被增強
        else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // The target does not implement the hashCode() method itself.
            return hashCode();
        }
        // 3、如果目標對象是DecoratingProxy類型,則返回目標對象的最終對象類型
        // DecoratingProxy接口只有一個getDecoratedClass方法,用于返回目標對象的最終對象類型
        else if (method.getDeclaringClass() == DecoratingProxy.class) {
            // There is only getDecoratedClass() declared -> dispatch to proxy config.
            return AopProxyUtils.ultimateTargetClass(this.advised);
        }
        // 4、如果目標對象是Advice類型,則直接使用反射進行調(diào)用
        // opaque-->標記是否需要阻止通過該配置創(chuàng)建的代理對象轉(zhuǎn)換為Advised類型,默認值為false,表示代理對象可以被轉(zhuǎn)換為Advised類型
        // method.getDeclaringClass().isInterface()-->目標對象是接口
        // method.getDeclaringClass().isAssignableFrom(Advised.class)-->
        // 是用來判斷一個類Class1和另一個類Class2是否相同或者Class1類是不是Class2的父類。例如:Class1.isAssignableFrom(Class2)
        else if (!this.advised.opaque
                && method.getDeclaringClass().isInterface()
                && method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // Service invocations on ProxyConfig with the proxy config...
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal;

        // 5、解決目標對象內(nèi)部自我調(diào)用無法實施切面增強,在這里暴露代理
        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        // Get as late as possible to minimize the time we "own" the target,
        // in case it comes from a pool.
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);

        // Get the interception chain for this method.
        // 6、獲取當(dāng)前方法的攔截器鏈,并執(zhí)行調(diào)用
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        // 檢測是否攔截器鏈是否為空,如果攔截器鏈為空,那么直接通過反射調(diào)用目標對象的方法,避免創(chuàng)建MethodInvocation
        // Check whether we have any advice. If we don't, we can fallback on direct
        // reflective invocation of the target, and avoid creating a MethodInvocation.
        if (chain.isEmpty()) {
            // We can skip creating a MethodInvocation: just invoke the target directly
            // Note that the final invoker must be an InvokerInterceptor so we know it does
            // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
            // 通過反射直接調(diào)用目標對象的方法
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        }
        else {
            // 創(chuàng)建MethodInvocation對象并調(diào)用proceed方法,攔截器鏈被封裝到了invocation中
            // We need to create a method invocation...
            invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // Proceed to the joinpoint through the interceptor chain.
            // 調(diào)用攔截器鏈
            retVal = invocation.proceed();
        }

        // 7、返回結(jié)果
        // Massage return value if necessary.
        Class<?> returnType = method.getReturnType();
        if (retVal != null
                && retVal == target
                && returnType != Object.class
                && returnType.isInstance(proxy)
                && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            // Special case: it returned "this" and the return type of the method
            // is type-compatible. Note that we can't help if the target sets
            // a reference to itself in another returned object.
            retVal = proxy;
        }
        else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
            throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);
        }
        return retVal;
    }
    finally {
        if (target != null && !targetSource.isStatic()) {
            // Must have come from TargetSource.
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

真正執(zhí)行攔截器鏈調(diào)用

public Object proceed() throws Throwable {
    //  We start with an index of -1 and increment early.
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }

    Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

    // 動態(tài)匹配增強
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // Evaluate dynamic method matcher here: static part will already have
        // been evaluated and found to match.
        InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        // 匹配成功則執(zhí)行
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        // 匹配失敗則跳過并執(zhí)行下一個攔截器
        else {
            // Dynamic matching failed.
            // Skip this interceptor and invoke the next in the chain.
            return proceed();
        }
    }
    // 靜態(tài)增強
    else {
        // It's an interceptor, so we just invoke it: The pointcut will have
        // been evaluated statically before this object was constructed.
        // System.out.println(interceptorOrInterceptionAdvice.getClass());
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

以上的代碼之前都已經(jīng)分析過,像其中的攔截器鏈獲取等,就不再詳細分析了,當(dāng)代碼執(zhí)行到這里的proceed方法里,如果我們沒有配置其他的AOP增強,那么第一個被執(zhí)行的攔截器就是TransactionInterceptor(事物攔截器)

3.TransactionInterceptor實現(xiàn)事物管理過程簡析
public Object invoke(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);
}
protected Object invokeWithinTransaction(Method method,
                                             @Nullable Class<?> targetClass,
                                             final InvocationCallback invocation) throws Throwable {
    // 1.準備工作
    // If the transaction attribute is null, the method is non-transactional.
    TransactionAttributeSource tas = getTransactionAttributeSource();
    // 獲取事物屬性
    final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
    // 獲取事物管理器
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    // 獲取目標類全限定類名+連接點方法名 例如:com.lyc.cn.v2.day09.AccountServiceImpl.save
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

    // 2.處理聲明式事物
    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        // Standard transaction demarcation with getTransaction and commit/rollback calls.
        // 2.2 創(chuàng)建事物
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
        System.out.println("==創(chuàng)建了名為:["+joinpointIdentification+"]的事物");
        Object retVal = null;
        try {
            // This is an around advice: Invoke the next interceptor in the chain.
            // This will normally result in a target object being invoked.
            // 2.3 繼續(xù)調(diào)用方法攔截器鏈,這里一般將會調(diào)用目標類的方法,如:com.lyc.cn.v2.day09.AccountServiceImpl.save方法
            retVal = invocation.proceedWithInvocation();
        } catch (Throwable ex) {
            // target invocation exception
            // 2.4 如果目標類方法拋出異常,則在此處理,例如:事物回滾
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        } finally {
            // 2.5 清除上一步創(chuàng)建的事物信息
            cleanupTransactionInfo(txInfo);
        }
        // 2.6 調(diào)用成功完成后執(zhí)行,但不是在異常被處理后執(zhí)行。如果我們不創(chuàng)建事務(wù),就什么也不做。
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }

    // 3.處理編程式事物
    else {
        final ThrowableHolder throwableHolder = new ThrowableHolder();
        // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
        try {
            Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {
                TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
                try {
                    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;
        }
    }
}

這里對聲明式事物和編程式事物分別做了不同的處理,我們主要看聲明式事物的實現(xiàn)過程,第一步的準備工作比較簡單,第二步是整個事物的核心所在。

  • 創(chuàng)建事物
  • 繼續(xù)調(diào)用方法攔截器鏈,這里一般將會調(diào)用目標類的方法,如:com.lyc.cn.v2.day09.AccountServiceImpl.save方法
  • 如果目標類方法拋出異常,則在此處理,例如:事物回滾
  • 清除上一步創(chuàng)建的事物信息
  • 調(diào)用成功完成后執(zhí)行commitTransactionAfterReturning方法,但不是在異常被處理后執(zhí)行。如果我們不創(chuàng)建事務(wù),就什么也不做。

分析到這里,我們也僅僅是得到了Spring事物管理的一些步驟,繼續(xù)分析,接下來就是Spring創(chuàng)建事物的過程了。這一部分的內(nèi)容太多了,還是分篇幅介紹吧。。。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容