淺入淺出代理模式與Spring事務管理

語言: CN / TW / HK

本文回顧了代理模式和Spring事務管理的原理。

前言

最近在開發業務代碼的時候,犯了一個事務註解的錯誤:在同一個類的非事務方法中調用了另一個事務方法,導致事務沒有生效,如下所示:

public ConfirmOrderResultVO batchConfirmPurchaseOrders(Long taobaoUserId, List<String> bizOrderIds) throws TCException {
………………………………………………………………
for (String bizOrderId : bizOrderIds) {
// 推單成功進入successList,否則進入failedList
if (confirmPurchaseOrder(taobaoUserId, bizOrderId)){
successList.add(Long.valueOf(bizOrderId));
}else {
failedList.add(Long.valueOf(bizOrderId));
}
}
………………………………………………………………
}

其中confirmPurchaseOrder()是一個事務方法:

@Transactional
public Boolean confirmPurchaseOrder(Long taobaoUserId, String bizOrderId) throws TCException {
logger.warn("|ConfirmPurchaseOrder|UserId:"+taobaoUserId+",orderId:"+bizOrderId);
…………………………
return ture;
}

這樣在直接調用batchConfirmPurchaseOrders()方法時,如果confirmPurchaseOrder()方法發生了異常,是不會回滾的。原理在於Spring的事務管理依靠的動態代理模式,當在同一個類中調用一個非事務方法,是不會生成代理對象的,自然也不會觸發事務。藉此機會回顧一下代理模式和Spring事務管理的原理。

代理模式

網上講解代理模式的文章千奇百怪,很多時候看完了也不明白講的重點是什麼。

事實上在生活中我們常常會遇到各種各樣的代理模式,例如火車票代售點代理出售火車票,他在“幫忙”出售火車票的同時,收取了額外的手續費,記錄了自己店裏的流水等。

又比如班長代理老師來上交班費,他在“上交班費”的動作之前,還進行了檢查班級同學是否到齊、向每一位同學收班費、核對班費金額,最後再上交班費。





總而言之,代理模式就是 代理其他對象,在完成原動作的基礎上(前後)做一些額外的自定義的工作

聰明的朋友看到這裏一定想到了:那我在一個方法中調用另一個方法不就是代理模式了嗎?啊對對對,從功能上來講是一樣的,但是“模式”之所以為“模式”,以我拙見,其本質目的在於形成規範,以減少重複代碼的編寫。因此如果不能達到減少代碼重複這一本質目的,便不能稱之為“模式”。

按照代理模式的實現方式,分為兩種:靜態代理和動態代理。那我們就拿剛剛説過的“寫作業”這件事來做例子,講解一下兩種實現方式的區別。

  靜態代理

首先定義一個“作業”接口,裏面有一個方法“做作業”

public interface Homework {
void doHomework();
}

小紅實現了這個接口。但是小紅是一個不愛學習的小學生,又因為師從馬掌門成為了學校的扛把子,所以為了不讓老師發現,他決定自己隨便做做,把剩下的部分交給小弟們來做。

public class XiaoHong implements Homework{
@Override
public void doHomework() {
System.out.println("XiaoHong did homework casually");
}
}


其中小王、小張兩個小弟成績優異,一個負責數學作業,一個負責英語作業,於是他們兩個自吿奮勇實現了Homework接口,在代理完成作業的基礎上,還不斷學習提高自己的能力,並且對作業答案進行了校驗。

小王:

public class XiaoWang implements Homework{


// 持有Homework屬性
private Homework homework;
// 通過構造函數初始化Homework
public XiaoWang(Homework homework){
this.homework=homework;
}


//代理實現
@Override
public void doHomework() {
doStudy();
homework.doHomework();
System.out.println("XiaoWang helps with MathHomework");
doCheck();
}


// 額外方法
private void doCheck() {
System.out.println("XiaoWang is checking-----------------");
}
// 額外方法
private void doStudy() {
System.out.println("XiaoWang is studying---------------");
}
}

小張:

public class XiaoZhang implements Homework{

// 持有Homework屬性
private Homework homework;
// 通過構造函數初始化Homework
public XiaoZhang(Homework homework){
this.homework=homework;
}


//代理實現
@Override
public void doHomework() {
doStudy();
homework.doHomework();
System.out.println("XiaoZhang helps with EngHomework");
doCheck();
}


// 額外方法
private void doCheck() {
System.out.println("XiaoZhang is checking-----------------");
}
// 額外方法
private void doStudy() {
System.out.println("XiaoZhang is studying---------------");
}
}

於是,小紅可以放心的把作業交給小王和小張了:

 public static void main(String[] args) {
// 實例化一個目標對象
XiaoHong xiaoHong = new XiaoHong();
// 把目標對象通過構造函數傳遞給代理對象
XiaoZhang xiaoZhang =new XiaoZhang(xiaoHong);
XiaoWang xiaoWang =new XiaoWang(xiaoHong);
// 調用代理對象的方法
xiaoZhang.doHomework();
xiaoWang.doHomework();
}

輸出:

XiaoZhang is studying---------------
XiaoHong did homework casually
XiaoZhang helps with EngHomework
XiaoZhang is checking-----------------

XiaoWang is studying---------------
XiaoHong did homework casually
XiaoWang helps with MathHomework
XiaoWang is checking-----------------

問題來了,如果老師又佈置了一個Book接口和readBook方法,但是readBook前後的動作是一樣的(都需要study和check),如何通過代理來實現呢?

一個方案是讓小王和小張都實現Book接口和readBook方法,並持有新的Book對象;另一個方案是再找一個小趙實現Book接口和readBook方法。

這樣兩種方案實際上都能達到效果,但是如果接口和方法增多起來呢?如果讓一個代理類實現一堆方法,並持有一堆不同的對象,這個類勢必會變得臃腫不堪;如果每一個新的方法都創建一個新的代理類,引用不同的對象,又會造成代碼的大量重複。因此,動態代理就出現了。

  動態代理

前文所講的靜態代理之所以為“靜態”,是因為每個接口的每個方法,我們都要顯式的去實現、創建、調用,所有的操作都是寫死的、是靜態的。而動態代理的巧妙之處在於,可以在程序的運行期間,動態的生成不同的代理類,來完成相同的工作。也就是説,如果你想實現一個方法,在所有其他方法執行前後做一些額外的相同的動作,例如打印日誌、記錄方法執行時間等,你就需要動態代理了(是不是想到了什麼?)。

事實上所有的動態代理思想都是一致的,而目前最常用的動態代理實現有兩種:JDK原生實現和Cglib開源實現。

Spring在5.X之前默認的動態代理實現一直是JDK動態代理。但是從5.X開始,Spring就開始默認使用Cglib來作為動態代理實現。並且SpringBoot從2.X開始也轉向了Cglib動態代理實現。

  • JDK實現動態代理

現在有一個牛X的機器人代理,可以幫所有人在完成老師佈置的任何任務前後,進行學習和答案的核對,在JDK的動態代理實現中,他是這樣編寫的:

// 實現InvocationHandler
public class RobotProxy implements InvocationHandler {


// 持有一個Object類型的目標對象target
private Object target;

// 通過構造函數實例化目標對象target
public RobotProxy(Object target) {
this.target = target;
}

// 重寫invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
doStudy();
// 執行目標對象的方法
Object invoke = method.invoke(target, args);
doCheck();
return invoke;
}

// 額外動作
private void doStudy() {
System.out.println("Robot is studying------------");
}
// 額外動作
private void doCheck() {
System.out.println("Robot is checking------------");
}
}

然後這樣調用機器人代理:

public static void main(String[] args) {
// 實例化一個目標對象
XiaoHong xiaoHong = new XiaoHong();
// 傳入實現的接口 new Class[]{Homework.class}
// 以及代理類 new RobotProxy(xiaoHong)
Homework homework = (Homework)Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Homework.class}, new RobotProxy(xiaoHong));
homework.doHomework();
}

輸出:

Robot is studying------------
XiaoHong did homework casually
Robot is checking------------

可以看到,JDK實現動態代理必須要依賴接口,只有實現了接口的目標類才可以被代理,而Cglib動態代理就可以消除接口的限制。

  • Cglib實現動態代理

創建一個升級版的機器人RovotV2代理類:

// 實現MethodInterceptor方法(要引入cglib包)
public class RobotV2Proxy implements MethodInterceptor {

// 重寫intercept方法
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
doStudy();
Object invoke = methodProxy.invokeSuper(o, objects);
doCheck();
return invoke;
}


// 額外動作
private void doStudy() {
System.out.println("RobotV2 is studying------------");
}
// 額外動作
private void doCheck() {
System.out.println("RobotV2 is checking------------");
}
}

調用方式:

public static void main(String[] args) {
// 創建增強類
Enhancer enhancer = new Enhancer();
// 設置父類
enhancer.setSuperclass(XiaoHong.class);
// 設置回調
enhancer.setCallback(new RobotV2Proxy());
// 創建目標對象
XiaoHong xiaoHong = (XiaoHong)enhancer.create();
// 調用目標方法
xiaoHong.doHomework();
}

輸出:

RobotV2 is studying------------
XiaoHong did homework casually
RobotV2 is checking------------

Cglib動態代理相比於JDK代理實現有幾個優勢:

  1. 基於字節碼,生成真實對象的子類。

  2. 運行效率高於JDK代理

  3. 不需要實現接口

可以看到,動態代理只需要一個代理類,就可以代理所有需要同一種處理(如上文所述的study()、check())的類和方法,這就是動態代理與靜態代理最大的區別。

Spring事務管理

OK,瞭解到了代理模式的好處之後,就可以自然而然的想到Spring事務管理的基本原理了。無非是藉助動態代理,做一些額外的操作:在真正的方法執行之前開啟事務,在方法順利執行完成之後提交事務,如果方法執行過程中發生異常,要執行回滾操作。

一般有兩種方法使用Spring的事務管理,一種是利用註解,一種是手動編程。本文因為使用的是註解型的事務管理,因此只對註解型事務的使用和原理進行探究,以找到事務沒有生效的原因。

  註解型事務

  • 註解型事務使用

註解型事務的使用非常簡單,只需要在需要事務管理的類或方法上加上@Transactional(rollbackFor = Exception.class),其中rollbackFor指定的是遇到什麼情況下執行回滾。

當然這個註解還有傳播級別、隔離級別等我們背過的八股文屬性,就不一一展開了。

  • 事務執行原理

PlatformTransactionManager

我們知道,事務是針對數據庫而言的。事實上,Spring並不直接管理事務,他只是提供了一套統一的框架和接口,具體由不同的、真正與數據庫打交道的持久層框架來實現,如Hibernate、Ibatis、Mybatis等,這個統一的接口是PlatformTransactionManager,它提供了三個核心方法:獲取事務(getTransaction)、提交事務(commit)、回滾事務(rollback),同時Spring提供了一個統一的抽象實現類AbstractPlatformTransactionManager,這是其他持久層框架事務實現類的共同父類。

public interface PlatformTransactionManager extends TransactionManager {

/**
* 獲取事務
/*
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;

/**
* 提交事務
/*
void commit(TransactionStatus status) throws TransactionException;


/**
* 回滾事務
/*
void rollback(TransactionStatus status) throws TransactionException;
}

先來看getTransaction方法:

@Override
public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException {


// 獲取事務定義(該類保存了當前事務的相關屬性,例如傳播規則、隔離規則等)
TransactionDefinition def = (definition != null ? definition : TransactionDefinition.withDefaults());
// 獲取真正的事務(doGetTransaction方法由具體的持久層框架實現)
Object transaction = doGetTransaction();
boolean debugEnabled = logger.isDebugEnabled();

// 如果已經存在事務,則根據傳播規則執行相應的處理
if (isExistingTransaction(transaction)) {
return handleExistingTransaction(def, transaction, debugEnabled);
}


// 檢查當前事務的超時時間
if (def.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", def.getTimeout());
}


// 如果當前不存在事務,檢查傳播級別和隔離級別,開始執行事務 startTransaction()
if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
else if (def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
def.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + def.getName() + "]: " + def);
}
try {
return startTransaction(def, transaction, debugEnabled, suspendedResources);
}
catch (RuntimeException | Error ex) {
resume(null, suspendedResources);
throw ex;
}
}
else {
// Create "empty" transaction: no actual transaction, but potentially synchronization.
if (def.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + def);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(def, null, true, newSynchronization, debugEnabled, null);
}
}

小結:getTransaction方法就是根據具體的傳播行為返回一個當前存在的事務或者一個新的事務對象



再來看提交事務方法commit:

  public final void commit(TransactionStatus status) throws TransactionException {
// 如果當前事務已經完成,拋出異常
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
// 如果當前事務設置了回滾標識,則進行回滾操作
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus, false);
return;
}
// 如果全局事務設置了回滾標識,則進行回滾操作(也就是事務嵌套的場景)
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus, true);
return;
}
// 提交事務
processCommit(defStatus);
}

小結:commit方法就是根據事務狀態提交事務,如果事務被標記為“回滾”態,則進行回滾操作



最後來看rollback方法:

public final void rollback(TransactionStatus status) throws TransactionException {
// 如果當前事務已經完成,拋出異常
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}


DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
processRollback(defStatus, false);
}

processRollback方法:

private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
try {
boolean unexpectedRollback = unexpected;


try {
triggerBeforeCompletion(status);
// 如果當前事務存在保存點,則回滾到保存點的狀態
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
}
// 如果是新的事務,則執行doRollback方法
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
doRollback(status);
}
else {
// 如果有嵌套事務,則執行doSetRollbackOnly方法
if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
doSetRollbackOnly(status);
}
else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
else {
logger.debug("Should roll back transaction but cannot - no transaction available");
}
// Unexpected rollback only matters here if we're asked to fail early
if (!isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = false;
}
}
}
catch (RuntimeException | Error ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}


triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);


// Raise UnexpectedRollbackException if we had a global rollback-only marker
// 發生了不期望的回滾異常
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
}
finally {
cleanupAfterCompletion(status);
}
}

這裏我們看到了一句熟悉的話"Transaction rolled back because it has been marked as rollback-only",相信一定有朋友在使用事務中看到過這個異常(不要犟),一般存在在這種場景下:A類的事務方法中調用了B類的事務方法,B方法發生了異常,但是在A類的try-catch模塊中捕獲了這個異常並正常執行。

這個異常發生的原因是:Spring默認的事務傳播級別是propagation.REQUIRED,即如果有沒有事務則新啟一個事務,如果已經存在事務則加入這個事務。B方法發生了異常,給當前事務打上了rollbackOnly標記,但是被A捕獲到了並正常執行,由於只有一個事務,等到A方法要提交這一個事務的時候,沒有發現異常但是發現事務被打上了rollbackOnly,只能回滾並拋出一個unexpectedRollback異常。

  • DynamicAdvisedInterceptor

現在,我們已經明確了事務的執行過程。那既然我們是使用註解來進行事務管理的,有了註解,就一定有註解的處理器,這時候就要搬出Spring的核心法寶——AOP。Spring就是通過切面來獲取所有的Spring註解、解析註解並處理註解下的類、方法等。而AOP的核心是動態代理,我們找到AOP包下的Cglib動態代理類CglibAopProxy,其中有內部靜態類DynamicAdvisedInterceptor

private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {


private final AdvisedSupport advised;


public DynamicAdvisedInterceptor(AdvisedSupport advised) {
this.advised = advised;
}


@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
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);
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
// no real advice, but just reflective invocation of the target.
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// 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.
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// We need to create a method invocation...
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
AopContext.setCurrentProxy(oldProxy);
}
}
}

其中有兩個核心方法:

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass)
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

第一個方法是獲取攔截鏈,第二個方法是創建攔截鏈的Cglib代理對象來執行。對於SpringAOP來講,凡是需要AOP增強的地方都會進入到這個方法,加載一系列的攔截器,按照責任鏈模式執行。所有攔截器都會實現MethodInterceptor接口,和invoke方法。

  • TransactionInterceptor

我們順藤摸瓜,找到了與事務相關的攔截器TransactionInterceptor,它實現了AOP包下的MethodInterceptor接口,和一個核心的調用方法invoke:

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
@Nullable
public Object invoke(final MethodInvocation invocation) throws Throwable {
// 獲取目標類
Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;
// 執行事務方法
return this.invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
@Nullable
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}


public Object getTarget() {
return invocation.getThis();
}


public Object[] getArguments() {
return invocation.getArguments();
}
});
}
}

invokeWithinTransaction

進入invokeWithinTransaction方法:

@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {


// If the transaction attribute is null, the method is non-transactional.
TransactionAttributeSource tas = getTransactionAttributeSource();
// 獲取事務屬性類TransactionAttribute
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 根據事務屬性選擇合適的事務管理器(如PlatformTransactionManager)
final TransactionManager tm = determineTransactionManager(txAttr);
………………………………………………………………………………
}

getTransactionAttribute

再看getTransactionAttribute方法:

public TransactionAttribute getTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
if (method.getDeclaringClass() == Object.class) {
return null;
}


// 從緩存中獲取TransactionAttribute
Object cacheKey = getCacheKey(method, targetClass);
TransactionAttribute cached = this.attributeCache.get(cacheKey);
if (cached != null) {
// Value will either be canonical value indicating there is no transaction attribute,
// or an actual transaction attribute.
if (cached == NULL_TRANSACTION_ATTRIBUTE) {
return null;
}
else {
return cached;
}
}
else {
// 如果緩存中沒有TransactionAttribute,則構建一個新的TransactionAttribute
TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
// Put it in the cache.
if (txAttr == null) {
this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
}
………………………………………………………………………………
}

computeTransactionAttribute

再進入到computeTransactionAttribute方法中:

  protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
// 校驗當前註解的方法是否是public的(也就是@Transactional註解的方法必須是public的)
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}


// 獲取真實目標對象的方法(獲取代理對象真正代理的目標對象的執行方法)
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);


// 從目標對象的方法中獲取事務屬性(其實就是在找註解,先找方法上有沒有註解)
TransactionAttribute txAttr = findTransactionAttribute(specificMethod);
if (txAttr != null) {
return txAttr;
}


// 從目標對象的類中獲取事務屬性(再找類上有沒有註解)
txAttr = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}


if (specificMethod != method) {
// Fallback is to look at the original method.
txAttr = findTransactionAttribute(method);
if (txAttr != null) {
return txAttr;
}
// Last fallback is the class of the original method.
txAttr = findTransactionAttribute(method.getDeclaringClass());
if (txAttr != null && ClassUtils.isUserLevelMethod(method)) {
return txAttr;
}
}


return null;
}


invokeWithinTransaction

拿到事務管理器後,再回到invokeWithinTransaction方法,我們看到一些核心代碼:

PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);


if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(ptm, 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) {
// 如果發生異常要進行回滾(內部是獲取相應的TransactionManager,執行rollback方法)
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 釋放事務資源
cleanupTransactionInfo(txInfo);
}


if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
}
// 提交事務(內部是獲取相應的TransactionManager,執行commit方法)
commitTransactionAfterReturning(txInfo);
return retVal;
}

總結

Spring通過AOP,在執行具有@Transactional註解的方法時,會根據目標類生成一個代理對象,在目標方法執行之前獲取事務並開啟事務,在目標方法執行之後提交事務或者回滾事務。當執行一個類中的普通方法時,Spring沒有生成代理對象,即使這個方法內部調用了同類其他的事務方法,也就無法參與事務管理了。



團隊介紹

天貓優品是天貓面向縣域下沉市場的新零售品牌,依託集團大中台,圍繞品牌-天貓優品平台-門店-消費者四個商業角色,構建了SBbc全鏈路新零售解決方案。自2020年6月迴歸天貓行業後,支撐了天貓優品直營業務的持續增長,同時沉澱了消電行業門店新零售數字化解決方案。

✿    拓展閲讀

作者 | 結森

編輯| 橙子君