Springboot源碼分析之事務攔截和管理

来源:https://www.cnblogs.com/qinzj/archive/2019/08/30/11437148.html
-Advertisement-
Play Games

摘要: 在 的自動裝配事務裡面, , ,`PlatformTransactionManager InfrastructureAdvisorAutoProxyCreator TransactionInterceptor Spring`事務的核心角色。 支撐著整個事務功能的架構,邏輯還是相對複雜的,那麼 ...


摘要:

springboot的自動裝配事務裡面,InfrastructureAdvisorAutoProxyCreator ,TransactionInterceptor,PlatformTransactionManager這三個bean都被裝配進來了,InfrastructureAdvisorAutoProxyCreator已經講過了,就是一個後置處理器,並且優先順序不是很高,而是最低,今天的重點是講解後面兩者之間在事務的扮演角色。TransactionInterceptor作為事務的增強子,扮演著增強處理Spring事務的核心角色。

TransactionInterceptor支撐著整個事務功能的架構,邏輯還是相對複雜的,那麼現在我們切入正題來分析此攔截器是如何實現事務特性的。

Spring事務三大介面

TransactionDefinition:用於描述隔離級別、超時時間、是否為只讀事務和事務傳播規則

    public interface TransactionDefinition {
        int PROPAGATION_REQUIRED = 0;
        int PROPAGATION_SUPPORTS = 1;
        int PROPAGATION_MANDATORY = 2;
        int PROPAGATION_REQUIRES_NEW = 3;
        int PROPAGATION_NOT_SUPPORTED = 4;
        int PROPAGATION_NEVER = 5;
        int PROPAGATION_NESTED = 6;
        int ISOLATION_DEFAULT = -1;
        int ISOLATION_READ_UNCOMMITTED = 1;
        int ISOLATION_READ_COMMITTED = 2;
        int ISOLATION_REPEATABLE_READ = 4;
        int ISOLATION_SERIALIZABLE = 8;
        int TIMEOUT_DEFAULT = -1;
    }

TransactionStatus:代表一個事務的具體運行狀態、以及保存點

    public interface TransactionStatus extends SavepointManager, Flushable {
       // 判斷當前的事務是否是新事務
        boolean isNewTransaction();
       // 判斷該事務裡面是否含有保存點
        boolean hasSavepoint();
         // 這是事務的唯一結果是否進行回滾。因此如果你在外層給try catche住不讓事務回滾,就會拋出你可能常見的異常
        void setRollbackOnly();
    
        boolean isRollbackOnly();
    
        void flush();
       // 不管是commit或者rollback了都算結束了~~~
        boolean isCompleted();
    }

一般都是使用它的實現類DefaultTransactionStatus,它是Spring預設使用的事務狀態。

PlatformTransactionManager:一個高層次的介面,看名字就知道是管理事務的

    public interface PlatformTransactionManager {
        TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
    
        void commit(TransactionStatus var1) throws TransactionException;
    
        void rollback(TransactionStatus var1) throws TransactionException;
    }

事務攔截器

    public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
        public TransactionInterceptor() {
        }
    
        public TransactionInterceptor(PlatformTransactionManager ptm, Properties attributes) {
            this.setTransactionManager(ptm);
            this.setTransactionAttributes(attributes);
        }
    
        public TransactionInterceptor(PlatformTransactionManager ptm, TransactionAttributeSource tas) {
            this.setTransactionManager(ptm);
            this.setTransactionAttributeSource(tas);
        }
    
      //最重要的方法,攔截入口
        @Nullable
        public Object invoke(MethodInvocation invocation) throws Throwable {
            Class<?> targetClass = invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null;
            Method var10001 = invocation.getMethod();
            invocation.getClass();
            return this.invokeWithinTransaction(var10001, targetClass, invocation::proceed);
        }
    //省略無關代碼......
    }

我們已經知道了,它是個MethodInterceptor,被事務攔截的方法最終都會執行到此增強器身上。
MethodInterceptor是個環繞通知,敲好符合我們的開啟、提交、回滾事務等操作,源碼分析可以看出,真正做事情的其實還是在父類,它有一個執行事務的模版。

TransactionAspectSupport

    public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {
        private static final Object DEFAULT_TRANSACTION_MANAGER_KEY = new Object();
        // currentTransactionStatus() 方法需要使用到它
        private static final ThreadLocal<TransactionAspectSupport.TransactionInfo> transactionInfoHolder = new NamedThreadLocal("Current aspect-driven transaction");
        protected final Log logger = LogFactory.getLog(this.getClass());
        //事務管理器的名稱
        @Nullable
        private String transactionManagerBeanName;
        //事務管理器
        @Nullable
        private PlatformTransactionManager transactionManager;
        //事務屬性源
        @Nullable
        private TransactionAttributeSource transactionAttributeSource;
        @Nullable
        private BeanFactory beanFactory;
        // 因為事務管理器可能也會有多個  所以此處做了一個簡單的緩存~
        private final ConcurrentMap<Object, PlatformTransactionManager> transactionManagerCache = new ConcurrentReferenceHashMap(4);
    
        public TransactionAspectSupport() {
        }
    
        @Nullable
        protected static TransactionAspectSupport.TransactionInfo currentTransactionInfo() throws NoTransactionException {
            return (TransactionAspectSupport.TransactionInfo)transactionInfoHolder.get();
        }
        //外部調用此Static方法,可議獲取到當前事務的狀態  從而甚至可議手動來提交、回滾事務
        public static TransactionStatus currentTransactionStatus() throws NoTransactionException {
            TransactionAspectSupport.TransactionInfo info = currentTransactionInfo();
            if (info != null && info.transactionStatus != null) {
                return info.transactionStatus;
            } else {
                throw new NoTransactionException("No transaction aspect-managed TransactionStatus in scope");
            }
        }
       //省略無關代碼......
      // 這裡可以發現,若傳入的為Properties  內部是實際使用的是NameMatchTransactionAttributeSource 去匹配的,transactionAttributeSource會被覆蓋的喲
        public void setTransactionAttributes(Properties transactionAttributes) {
            NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource();
            tas.setProperties(transactionAttributes);
            this.transactionAttributeSource = tas;
        }
        // 根據方法和目標類來選擇
        public void setTransactionAttributeSources(TransactionAttributeSource... transactionAttributeSources) {
            this.transactionAttributeSource = new CompositeTransactionAttributeSource(transactionAttributeSources);
        }
       //省略無關代碼......
      // 接下來就只剩我們最為核心的處理事務的模版方法了
       @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();
        // 獲取該方法對應的事務屬性(這個特別重要)
       // 不同的事務處理方式使用不同的邏輯。對於聲明式事務的處理與編程式事務的處理,重要區別在於事務屬性上,因為編程式的事務處理是不需要有事務屬性的
            final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
        // 找到一個合適的事務管理器
            final PlatformTransactionManager tm = determineTransactionManager(txAttr);
        // 拿到目標方法唯一標識
            final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
    
            if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
                // Standard transaction demarcation with getTransaction and commit/rollback calls.
          // 看是否有必要創建一個事務,根據`事務傳播行為`,做出相應的判斷
                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
            // 出現異常了,進行回滾(註意:並不是所有異常都會rollback的)
                    // 備註:此處若沒有事務屬性   會commit 相容編程式事務吧
                    completeTransactionAfterThrowing(txInfo, ex);
                    throw ex;
                }
                finally {
            //清除信息
                    cleanupTransactionInfo(txInfo);
                }
          // 目標方法完全執行完成後,提交事務~~~
                commitTransactionAfterReturning(txInfo);
                return retVal;
            }
            else {
          //編程式事務處理(CallbackPreferringPlatformTransactionManager) 會走這裡 
            // 原理也差不太多,這裡不做詳解~~~~
        
                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;
                }
            }
        }
      // 從容器中找到一個事務管理器
        @Nullable
        protected PlatformTransactionManager determineTransactionManager(@Nullable TransactionAttribute txAttr) {
            if (txAttr != null && this.beanFactory != null) {
              // qualifier 就在此處發揮作用了,他就相當於BeanName
                String qualifier = txAttr.getQualifier();
                if (StringUtils.hasText(qualifier)) {
                  // 根據此名稱 以及PlatformTransactionManager.class 去容器內找
                    return this.determineQualifiedTransactionManager(this.beanFactory, qualifier);  // 若沒有指定qualifier   那再看看是否指定了 transactionManagerBeanName
                } else if (StringUtils.hasText(this.transactionManagerBeanName)) {
                  
                    return this.determineQualifiedTransactionManager(this.beanFactory, this.transactionManagerBeanName);
                } else {
                  // 若都沒指定,那就不管了。直接根據類型去容器里找 getBean(Class)
            // 此處:若容器內有兩個PlatformTransactionManager ,那就鐵定會報錯啦~~~
        
                    PlatformTransactionManager defaultTransactionManager = this.getTransactionManager();
                    if (defaultTransactionManager == null) {
                        defaultTransactionManager = (PlatformTransactionManager)this.transactionManagerCache.get(DEFAULT_TRANSACTION_MANAGER_KEY);
                        if (defaultTransactionManager == null) {
                            defaultTransactionManager = (PlatformTransactionManager)this.beanFactory.getBean(PlatformTransactionManager.class);
                            this.transactionManagerCache.putIfAbsent(DEFAULT_TRANSACTION_MANAGER_KEY, defaultTransactionManager);
                        }
                    }
    
                    return defaultTransactionManager;
                }
            } else {
              // 如果這兩個都沒配置,所以肯定是手動設置了PlatformTransactionManager的,那就直接返回即可
                return this.getTransactionManager();
            }
        }
    
        private PlatformTransactionManager determineQualifiedTransactionManager(BeanFactory beanFactory, String qualifier) {
            PlatformTransactionManager txManager = (PlatformTransactionManager)this.transactionManagerCache.get(qualifier);
            if (txManager == null) {
                txManager = (PlatformTransactionManager)BeanFactoryAnnotationUtils.qualifiedBeanOfType(beanFactory, PlatformTransactionManager.class, qualifier);
                this.transactionManagerCache.putIfAbsent(qualifier, txManager);
            }
    
            return txManager;
        }
    
        private String methodIdentification(Method method, @Nullable Class<?> targetClass, @Nullable TransactionAttribute txAttr) {
            String methodIdentification = this.methodIdentification(method, targetClass);
            if (methodIdentification == null) {
                if (txAttr instanceof DefaultTransactionAttribute) {
                    methodIdentification = ((DefaultTransactionAttribute)txAttr).getDescriptor();
                }
    
                if (methodIdentification == null) {
                    methodIdentification = ClassUtils.getQualifiedMethodName(method, targetClass);
                }
            }
    
            return methodIdentification;
        }
    
        @Nullable
        protected String methodIdentification(Method method, @Nullable Class<?> targetClass) {
            return null;
        }
    // 若有需要 創建一個TransactionInfo (具體的事務從事務管理器裡面getTransaction())
        protected TransactionAspectSupport.TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, final String joinpointIdentification) {
          //賦值
            if (txAttr != null && ((TransactionAttribute)txAttr).getName() == null) {
                txAttr = new DelegatingTransactionAttribute((TransactionAttribute)txAttr) {
                    public String getName() {
                        return joinpointIdentification;
                    }
                };
            }
    // 從事務管理器里,通過txAttr拿出來一個TransactionStatus
            TransactionStatus status = null;
            if (txAttr != null) {
                if (tm != null) {
                    status = tm.getTransaction((TransactionDefinition)txAttr);
                } else if (this.logger.isDebugEnabled()) {
                    this.logger.debug("Skipping transactional joinpoint [" + joinpointIdentification + "] because no transaction manager has been configured");
                }
            }
    // 通過TransactionStatus 等,轉換成一個通用的TransactionInfo
            return this.prepareTransactionInfo(tm, (TransactionAttribute)txAttr, joinpointIdentification, status);
        }
        protected TransactionAspectSupport.TransactionInfo prepareTransactionInfo(@Nullable PlatformTransactionManager tm, @Nullable TransactionAttribute txAttr, String joinpointIdentification, @Nullable TransactionStatus status) {
          //構造一個TransactionInfo
            TransactionAspectSupport.TransactionInfo txInfo = new TransactionAspectSupport.TransactionInfo(tm, txAttr, joinpointIdentification);
            if (txAttr != null) {
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
                }
               // 設置事務狀態
                txInfo.newTransactionStatus(status);
            } else if (this.logger.isTraceEnabled()) {
                this.logger.trace("No need to create transaction for [" + joinpointIdentification + "]: This method is not transactional.");
            }
    // 這句話是最重要的,把生成的TransactionInfo並綁定到當前線程的ThreadLocal
            txInfo.bindToThread();
            return txInfo;
        }
    //比較簡單  只用用事務管理器提交事務即可~~~  具體的實現邏輯在事務管理器的commit實現里~~
        protected void commitTransactionAfterReturning(@Nullable TransactionAspectSupport.TransactionInfo txInfo) {
            if (txInfo != null && txInfo.getTransactionStatus() != null) {
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
                }
    
                txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
            }
    
        }
    
        protected void completeTransactionAfterThrowing(@Nullable TransactionAspectSupport.TransactionInfo txInfo, Throwable ex) {
            if (txInfo != null && txInfo.getTransactionStatus() != null) {
                if (this.logger.isTraceEnabled()) {
                    this.logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "] after exception: " + ex);
                }
    // 如果有事務屬性了,那就調用rollbackOn看看這個異常需不需要回滾
                if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
                    try {
                        txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
                    } catch (TransactionSystemException var6) {
                        this.logger.error("Application exception overridden by rollback exception", ex);
                        var6.initApplicationException(ex);
                        throw var6;
                    } catch (Error | RuntimeException var7) {
                        this.logger.error("Application exception overridden by rollback exception", ex);
                        throw var7;
                    }
                } else {
                  // 編程式事務沒有事務屬性,那就commit吧
                    try {
                        txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
                    } catch (TransactionSystemException var4) {
                        this.logger.error("Application exception overridden by commit exception", ex);
                        var4.initApplicationException(ex);
                        throw var4;
                    } catch (Error | RuntimeException var5) {
                        this.logger.error("Application exception overridden by commit exception", ex);
                        throw var5;
                    }
                }
            }
    
        }
    
        protected void cleanupTransactionInfo(@Nullable TransactionAspectSupport.TransactionInfo txInfo) {
            if (txInfo != null) {
                txInfo.restoreThreadLocalStatus();
            }
    
        }
    
        private static class ThrowableHolderException extends RuntimeException {
            public ThrowableHolderException(Throwable throwable) {
                super(throwable);
            }
    
            public String toString() {
                return this.getCause().toString();
            }
        }
    
        private static class ThrowableHolder {
            @Nullable
            public Throwable throwable;
    
            private ThrowableHolder() {
            }
        }
    
        @FunctionalInterface
        protected interface InvocationCallback {
            Object proceedWithInvocation() throws Throwable;
        }
    
        protected final class TransactionInfo {
          // 當前事務  的事務管理器
            @Nullable
            private final PlatformTransactionManager transactionManager;
          // 當前事務  的事務屬性
            @Nullable
            private final TransactionAttribute transactionAttribute;
          //joinpoint標識
            private final String joinpointIdentification;
          //當前事務    的TransactionStatus
            @Nullable
            private TransactionStatus transactionStatus;
          // 重點就是這個oldTransactionInfo欄位
              // 這個欄位保存了當前事務所在的`父事務`上下文的引用,構成了一個鏈,準確的說是一個有向無環圖
        
            @Nullable
            private TransactionAspectSupport.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;
            }
    
            @Nullable
            public TransactionAttribute getTransactionAttribute() {
                return this.transactionAttribute;
            }
    
            public String getJoinpointIdentification() {
                return this.joinpointIdentification;
            }
            //註意這個方法名,新的一個事務status
            public void newTransactionStatus(@Nullable TransactionStatus status) {
                this.transactionStatus = status;
            }
    
            @Nullable
            public TransactionStatus getTransactionStatus() {
                return this.transactionStatus;
            }
    
            public boolean hasTransaction() {
                return this.transactionStatus != null;
            }
             //綁定當前正在處理的事務的所有信息到ThreadLocal
            private void bindToThread() {
              // 老的事務  先從線程中拿出來,再把新的(也就是當前)綁定進去~~~~~~
                this.oldTransactionInfo = (TransactionAspectSupport.TransactionInfo)TransactionAspectSupport.transactionInfoHolder.get();
                TransactionAspectSupport.transactionInfoHolder.set(this);
            }
            //當前事務處理完之後,恢復父事務上下文
            private void restoreThreadLocalStatus() {
                TransactionAspectSupport.transactionInfoHolder.set(this.oldTransactionInfo);
            }
    
            public String toString() {
                return this.transactionAttribute != null ? this.transactionAttribute.toString() : "No transaction";
            }
        }
    }

事務管理器

file

AbstractPlatformTransactionManager

可見它是對PlatformTransactionManager的一個抽象實現。實現Spring的標準事務工作流
這個基類提供了以下工作流程處理:

  • 確定如果有現有的事務;
  • 應用適當的傳播行為;
  • 如果有必要暫停和恢復事務;
  • 提交時檢查rollback-only標記;
  • 應用適當的修改當回滾(實際回滾或設置rollback-only);
    觸發同步回調註冊(如果事務同步是激活的)
    public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {
    
        //始終激活事務同步(請參閱事務的傳播屬性~)
        public static final int SYNCHRONIZATION_ALWAYS = 0;
        //僅對實際事務(即,不針對由傳播導致的空事務)激活事務同步\不支持現有後端事務
        public static final int SYNCHRONIZATION_ON_ACTUAL_TRANSACTION = 1;
        //永遠不激活事務同步
        public static final int SYNCHRONIZATION_NEVER = 2;
    
        // 相當於把本類的所有的public static final的變數都收集到此處~~~~
        private static final Constants constants = new Constants(AbstractPlatformTransactionManager.class);
    
        // ===========預設值
        private int transactionSynchronization = SYNCHRONIZATION_ALWAYS;
        // 事務預設的超時時間  為-1表示不超時
        private int defaultTimeout = TransactionDefinition.TIMEOUT_DEFAULT;
        //Set whether nested transactions are allowed. Default is "false".
        private boolean nestedTransactionAllowed = false;
        // Set whether existing transactions should be validated before participating(參與、加入)
        private boolean validateExistingTransaction = false;
        
        //設置是否僅在參與事務`失敗後`將 現有事務`全局`標記為回滾  預設值是true 需要註意~~~
        // 表示只要你的事務失敗了,就標記此事務為rollback-only 表示它只能給與回滾  而不能再commit或者正常結束了
        // 這個調用者經常會犯的一個錯誤就是:上層事務service拋出異常了,自己把它給try住,並且並且還不throw,那就肯定會報錯的:
        // 報錯信息:Transaction rolled back because it has been marked as rollback-only
        // 當然嘍,這個屬性強制不建議設置為false~~~~~~
        private boolean globalRollbackOnParticipationFailure = true;
        // 如果事務被全局標記為僅回滾,則設置是否及早失敗~~~~
        private boolean failEarlyOnGlobalRollbackOnly = false;
        // 設置在@code docommit調用失敗時是否應執行@code dorollback 通常不需要,因此應避免
        private boolean rollbackOnCommitFailure = false;
        
        // 我們發現使用起來有點枚舉的意思了,特別是用XML配置的時候  非常像枚舉的使用~~~~~~~
        // 這也是Constants的重要意義~~~~
        public final void setTransactionSynchronizationName(String constantName) {
            setTransactionSynchronization(constants.asNumber(constantName).intValue());
        }
        public final void setTransactionSynchronization(int transactionSynchronization) {
            this.transactionSynchronization = transactionSynchronization;
        }
        //... 省略上面所有欄位的一些get/set方法~~~
    
        // 最為重要的一個方法,根據實物定義,獲取到一個事務TransactionStatus 
        @Override
        public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
            //doGetTransaction()方法是抽象方法,具體的實現由具體的事務處理器提供(下麵會以DataSourceTransactionManager為例子)
            Object transaction = doGetTransaction();
    
            //如果沒有配置事務屬性,則使用預設的事務屬性
            if (definition == null) {
                definition = new DefaultTransactionDefinition();
            }
    
            //檢查當前線程是否存在事務  isExistingTransaction此方法預設返回false  但子類都覆寫了此方法
            if (isExistingTransaction(transaction)) {
                // handleExistingTransaction方法為處理已經存在事務的情況
                // 這個方法的實現也很複雜,總之還是對一些傳播屬性進行解析,各種情況的考慮~~~~~ 如果有新事務產生 doBegin()就會被調用~~~~
                return handleExistingTransaction(definition, transaction, debugEnabled);
            }
    
            // 超時時間的簡單校驗~~~~
            if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
                throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
            }
    
            // 處理事務屬性中配置的事務傳播特性==============
        
            // PROPAGATION_MANDATORY 如果已經存在一個事務,支持當前事務。如果沒有一個活動的事務,則拋出異常
            if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
                throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");
            }
        
            //如果事務傳播特性為required、required_new或nested
            else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
                    definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
                    definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
                    
                // 掛起,但是doSuspend()由子類去實現~~~
                // 掛起操作,觸發相關的掛起註冊的事件,把當前線程事物的所有屬性都封裝好,放到一個SuspendedResourcesHolder
                // 然後清空清空一下`當前線程事務`
                SuspendedResourcesHolder suspendedResources = suspend(null);
    
                // 此處,開始創建事務~~~~~
                try {
                    boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
    
                    // //創建一個新的事務狀態  就是new DefaultTransactionStatus()  把個屬性都賦值上
                    DefaultTransactionStatus status = newTransactionStatus(
                            definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
                    // 開始事務,抽象方法,由子類去實現~
                    doBegin(transaction, definition);
                    //初始化和同步事務狀態    是TransactionSynchronizationManager這個類  它內部維護了很多的ThreadLocal
                    prepareSynchronization(status, definition);
                    return status;
                }
                catch (RuntimeException | Error ex) {
                    //重新開始 doResume由子類去實現
                    resume(null, suspendedResources);
                    throw ex;
                }
            }
            // 走到這裡  傳播屬性就是不需要事務的  那就直接創建一個
            else {
                boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
                // 這個方法相當於先newTransactionStatus,再prepareSynchronization這兩步~~~
                // 顯然和上面的區別是:中間不回插入調用doBegin()方法,因為沒有事務  begin個啥~~
                return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
            }
        }
    
    
        // 再看看commit方法
        @Override
        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()) {
                processRollback(defStatus, false);
                return;
            }
    
            //  shouldCommitOnGlobalRollbackOnly這個預設值是false,目前只有JTA事務覆寫成true了
            // isGlobalRollbackOnly:是否標記為了全局的RollbackOnly
            if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
                processRollback(defStatus, true);
                return;
            }
            // 提交事務   這裡面還是挺複雜的,會考慮到還原點、新事務、事務是否是rollback-only之類的~~
            processCommit(defStatus);
        }
    
        // rollback方法  裡面doRollback方法交給子類去實現~~~
        @Override
        public final void rollback(TransactionStatus status) throws TransactionException {
            DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
            processRollback(defStatus, false);
        }
    }

從這個抽象類源碼分析可以看出,它絕對是一個非常非常典型的模版實現,各個方法實現都是這樣。自己先提供實現模版,很多具體的實現方案都開放給子類,比如begin,suspend, resume,commit,rollback等,相當於留好了眾多的連接點

DataSourceTransactionManager

    // 它還實現了ResourceTransactionManager介面,提供了getResourceFactory()方法
    public class DataSourceTransactionManager extends AbstractPlatformTransactionManager implements ResourceTransactionManager, InitializingBean {
        // 顯然它管理的就是DataSource  而JTA分散式事務管理可能就是各種各樣的數據源了
        @Nullable
        private DataSource dataSource;
        // 不要強制標記為ReadOnly
        private boolean enforceReadOnly = false;
    
        // JDBC預設是允許內嵌的事務的
        public DataSourceTransactionManager() {
            setNestedTransactionAllowed(true);
        }
        public DataSourceTransactionManager(DataSource dataSource) {
            this();
            setDataSource(dataSource);
            // 它自己的InitializingBean也是做了一個簡單的校驗而已~~~
            afterPropertiesSet();
        }
    
        // 手動設置數據源
        public void setDataSource(@Nullable DataSource dataSource) {
            // 這步處理有必要
            // TransactionAwareDataSourceProxy是對dataSource 的包裝
            if (dataSource instanceof TransactionAwareDataSourceProxy) {
                this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
            } else {
                this.dataSource = dataSource;
            }
        }
    
        //Return the JDBC DataSource
        @Nullable
        public DataSource getDataSource() {
            return this.dataSource;
        }
        // @since 5.0 Spring5.0提供的方法   其實還是調用的getDataSource()  判空了而已
        protected DataSource obtainDataSource() {
            DataSource dataSource = getDataSource();
            Assert.state(dataSource != null, "No DataSource set");
            return dataSource;
        }
        // 直接返回的數據源~~~~
        @Override
        public Object getResourceFactory() {
            return obtainDataSource();
        }
        ...
        // 這裡返回的是一個`DataSourceTransactionObject`
        // 它是一個`JdbcTransactionObjectSupport`,所以它是SavepointManager、實現了SmartTransactionObject介面
        @Override
        protected Object doGetTransaction() {
            DataSourceTransactionObject txObject = new DataSourceTransactionObject();
            txObject.setSavepointAllowed(isNestedTransactionAllowed());
            // 這個獲取有意思~~~~相當於按照線程來的~~~
            ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
            txObject.setConnectionHolder(conHolder, false);
            return txObject;
        }
    
        // 檢查當前事務是否active
        @Override
        protected boolean isExistingTransaction(Object transaction) {
            DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
            return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
        }
    
    
        // 這是一個核心內容了,裡面邏輯需要分析分析~~~
        @Override
        protected void doBegin(Object transaction, TransactionDefinition definition) {
            DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
            Connection con = null;
    
            try {
                if (!txObject.hasConnectionHolder() || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
                    // 從DataSource里獲取一個連接(這個DataSource一般是有連接池的~~~)
                    Connection newCon = obtainDataSource().getConnection();
                    // 把這個鏈接用ConnectionHolder包裝一下~~~
                    txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
                }
    
                txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
                con = txObject.getConnectionHolder().getConnection();
                
                // 設置isReadOnly、設置隔離界別等~
                Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
                txObject.setPreviousIsolationLevel(previousIsolationLevel);
    
                // 這裡非常的關鍵,先看看Connection 是否是自動提交的
                // 如果是 就con.setAutoCommit(false)  要不然資料庫預設沒執行一條SQL都是一個事務,就沒法進行事務的管理了
                if (con.getAutoCommit()) {
                    txObject.setMustRestoreAutoCommit(true);
                    con.setAutoCommit(false);
                }
                // ====因此從這後面,通過此Connection執行的所有SQL語句只要沒有commit就都不會提交給資料庫的=====
                
                // 這個方法特別特別有意思   它自己`Statement stmt = con.createStatement()`拿到一個Statement
                // 然後執行了一句SQL:`stmt.executeUpdate("SET TRANSACTION READ ONLY");`
                // 所以,所以:如果你僅僅只是查詢。把事務的屬性設置為readonly=true  Spring對幫你對SQl進行優化的
                // 需要註意的是:readonly=true 後,只能讀,不能進行dml操作)(只能看到設置事物前數據的變化,看不到設置事物後數據的改變)
                prepareTransactionalConnection(con, definition);
                txObject.getConnectionHolder().setTransactionActive(true);
    
                int timeout = determineTimeout(definition);
                if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
                    txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
                }
    
                // Bind the connection holder to the thread.
                // 這一步:就是把當前的鏈接 和當前的線程進行綁定~~~~
                if (txObject.isNewConnectionHolder()) {
                    TransactionSynchronizationManager.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);
            }
        }
    
        // 真正提交事務
        @Override
        protected void doCommit(DefaultTransactionStatus status) { DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
            // 拿到鏈接  然後直接就commit了   
            Connection con = txObject.getConnectionHolder().getConnection();
            try {
                con.commit();
            } catch (SQLException ex) {
                throw new TransactionSystemException("Could not commit JDBC transaction", ex);
            }
        }
        //doRollback()方法也類似  這裡不再細說
    }

小結:

事務屬性readonly=true後,只能讀操作)(只能看到設置事物前數據的變化,看不到設置事物後數據的改變) 但是通過源碼我發現,你只設置@Transactional(readOnly = true)這樣是不夠的,還必須在配置DataSourceTransactionManager的時候,來這麼一句dataSourceTransactionManager.setEnforceReadOnly(true),最終才會對你的只讀事務進行優化~~~~
其實如果僅僅只是設置@Transactional(readOnly = true),最終會把這個Connection設置為只讀:con.setReadOnly(true); 它表示將此連接設置為只讀模式,作為驅動程式啟用資料庫優化的提示。 將鏈接設置為只讀模式通知資料庫後,資料庫會對做自己的只讀優化。但是,這對資料庫而言不一定對於資料庫而言這就是readonly事務,這點是非常重要的。(因為畢竟一個事務內可能有多個鏈接.


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • java代碼是否一定按順序執行? 這個問題聽起來有點蠢,串列的代碼確實會按代碼語意正確的執行,但是編譯器對於代碼本身的優化卻並不一定會按實際的代碼一步一步的執行。 比如: r1=a; r2=r1.x; r3=r1.x; 編譯器則可能會進行優化,將r3=r1.x這條指令替換成r3=r2,這就是指令的重 ...
  • 函數中使用全局變數 執行fun()後返回值為:500 a, b使用的是全局變數的值。 函數中覆蓋全局變數 執行fun()後返回值為:50 a, b使用的是局部變數的值。 函數中修改改全局變數 執行fun()後返回值為:110 a使用的是全局變數a=100的值, b使用的是函數func修改後b的值10 ...
  • 一、基本選擇器 1.標簽選擇器 標簽選擇器會選中所有的標簽元素,是“共性”,而不是“特性”。 2.id選擇器 由於同一個頁面中id不能重覆,所以id選中的是特性,而不是共性。 3.類選擇器 由於同一個頁面中類名可以重覆,所以選中的是共性。 重點:公共類 到底是使用id還是class? 儘可能使用cl ...
  • HttpSessionActivationListener 監聽HttpSession對象的活化、鈍化 鈍化:將HttpSession對象從記憶體中轉移至硬碟,存儲為.session文件。 活化:把HttpSession對象從持久化狀態轉變為運行狀態(從硬碟載入到記憶體,恢複原樣)。 HttpSessi ...
  • 12.33 Django框架簡介: MVC,全名是Model View Controller,是軟體工程中的一種軟體架構模式,把軟體系統分為三個基本部分:模型(Model)、視圖(View)和控制器(Controller),具有耦合性低、重用性高、生命周期成本低等優點 Django框架的設計模式借鑒 ...
  • 1.下載idlex-1.18.zip 網址:https://zh.osdn.net/projects/sfnet_idlex/ 2.解壓idlex-1.18.zip 2.解壓idlex-1.18.zip (1)將LineNumbers.py(idlex-1.18\idlexlib\extension ...
  • 緩存雪崩 緩存雪崩是由於原有緩存失效(過期),新緩存未到期間。所有請求都去查詢資料庫,而對資料庫CPU和記憶體造成巨大壓力,嚴重的會造成資料庫宕機。從而形成一系列連鎖反應,造成整個系統崩潰。 解決方法: 一般併發量不是特別多的時候,使用最多的解決方案是加鎖排隊。 給每一個緩存數據增加相應的緩存標記,記 ...
  • 安裝 urllib庫更適合寫爬蟲文件,scrapy更適合做爬蟲項目。 步驟: 1. 先更改pip源,國外的太慢了,參考:https://www.jb51.net/article/159167.htm 2. 升級pip:python m pip install upgrade pip 3. pip i ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...