成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

重拾-Spring Transaction

fasss / 1516人閱讀

摘要:當(dāng)存在掛起的事務(wù)時(shí),執(zhí)行恢復(fù)掛起的事務(wù)將掛起的事務(wù)綁定的重新綁定到當(dāng)前上下文事務(wù)的就是將掛起的事務(wù)重新綁定到當(dāng)前上下文中。

問(wèn)題

面試中是不是有時(shí)經(jīng)常會(huì)被問(wèn)到 “Spring 事務(wù)如何管理的了解嗎?” ,“Spring 事務(wù)的傳播性有哪些,能聊聊它們的使用場(chǎng)景嗎?”, “事務(wù)回滾的時(shí)候是所有異常下都會(huì)回滾嗎?”; 下面我們就帶著這些問(wèn)題來(lái)看看 Spring 事務(wù)是如何實(shí)現(xiàn)的吧。

實(shí)現(xiàn)分析

首先我們還是先通過(guò)一個(gè)使用示例,先看下 Spring 事務(wù)是如何工作的。

使用示例
本文我們先采用 TransactionProxyFactoryBean 配置的方式來(lái)看下, Spring 事務(wù)如何實(shí)現(xiàn)

    
    
        
            com.mysql.jdbc.Driver
        
        
            url
        
        
            username
        
        
            password
        
    

    
    
        
            
        
    

    
    
        
            
        
    

    
        
            
        
    

    
    
        
            
        

        
            
        

        
            org.springframework.transaction.UserService
        
        
        
            
                PROPAGATION_REQUIRED
            
        
    

TransactionProxyFactoryBean 的屬性配置中如果您對(duì) transactionAttributes 屬性不熟悉的話,是不是會(huì)感覺(jué)一頭霧水呢? 這個(gè)玩意怎么配置的? 配置格式又是什么樣的呢? 配置值有哪些呢 ?; 下面將會(huì)通過(guò)對(duì) TransactionProxyFactoryBean 的源碼分析來(lái)一一解答。

源碼分析 類結(jié)構(gòu)

TransactionProxyFactoryBean 類結(jié)構(gòu)我們知道,其實(shí)現(xiàn)了接口 InitializingBeanFactoryBean; 那么也就是在 TransactionProxyFactoryBean 實(shí)例化后會(huì)調(diào)用方法 afterPropertiesSet, 在獲取目標(biāo)對(duì)象實(shí)例時(shí)會(huì)調(diào)用方法 getObject; 下面將主要看下這兩個(gè)方法的實(shí)現(xiàn)。

afterPropertiesSet-創(chuàng)建目標(biāo)代理對(duì)象
public void afterPropertiesSet() throws AopConfigException {
    // 校驗(yàn) Target 目標(biāo)對(duì)象
    if (this.target == null) {
        throw new AopConfigException("Target must be set");
    }

    // 校驗(yàn)事務(wù)屬性定義,從拋出的異常信息可以看出 Spring 在此做了強(qiáng)校驗(yàn);
    // 也就是說(shuō)如果沒(méi)有需要 Spring 事務(wù)管理的方法,就不要采用 TransactionProxyFactoryBean 了
    // 那么 transactionAttributeSource 是怎么來(lái)的呢? 見(jiàn)下文分析
    if (this.transactionAttributeSource == null) {
        throw new AopConfigException("Either "transactionAttributeSource" or "transactionAttributes" is required: " +
                                                                 "If there are no transactional methods, don"t use a transactional proxy.");
    }

    // 創(chuàng)建事務(wù)攔截器 transactionInterceptor
    TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
    transactionInterceptor.setTransactionManager(this.transactionManager);
    transactionInterceptor.setTransactionAttributeSource(this.transactionAttributeSource);
    transactionInterceptor.afterPropertiesSet();

    ProxyFactory proxyFactory = new ProxyFactory();

    // 是否配置了前置攔截
    if (this.preInterceptors != null) {
        for (int i = 0; i < this.preInterceptors.length; i++) {
            proxyFactory.addAdvisor(GlobalAdvisorAdapterRegistry.getInstance().wrap(this.preInterceptors[i]));
        }
    }

    if (this.pointcut != null) {
        // 如果配置了 pointcut 切入點(diǎn),則按配置的 pointcut 創(chuàng)建 advisor
        Advisor advice = new DefaultPointcutAdvisor(this.pointcut, transactionInterceptor);
        proxyFactory.addAdvisor(advice);
    }
    else {
        // rely on default pointcut
        // 創(chuàng)建事務(wù)攔截切面 advisor
        proxyFactory.addAdvisor(new TransactionAttributeSourceAdvisor(transactionInterceptor));
        // could just do the following, but it"s usually less efficient because of AOP advice chain caching
        // proxyFactory.addInterceptor(transactionInterceptor);
    }

    // 是否配置了后置攔截
    if (this.postInterceptors != null) {
        for (int i = 0; i < this.postInterceptors.length; i++) {
            proxyFactory.addAdvisor(GlobalAdvisorAdapterRegistry.getInstance().wrap(this.postInterceptors[i]));
        }
    }

    proxyFactory.copyFrom(this);

    proxyFactory.setTargetSource(createTargetSource(this.target));
    // 設(shè)置代理的接口
    if (this.proxyInterfaces != null) {
        proxyFactory.setInterfaces(this.proxyInterfaces);
    }
    else if (!getProxyTargetClass()) {
        // rely on AOP infrastructure to tell us what interfaces to proxy
        proxyFactory.setInterfaces(AopUtils.getAllInterfaces(this.target));
    }
    // 創(chuàng)建目標(biāo)對(duì)象的代理對(duì)象
    this.proxy = proxyFactory.getProxy();
}

從源碼中我們知道 afterPropertiesSet 主要做以下幾件事:

參數(shù)有效性校驗(yàn); 校驗(yàn)?zāi)繕?biāo)對(duì)象,事務(wù)屬性定義

設(shè)置代理的 advisor chain, 包括用戶自定義的前置攔截, 內(nèi)置的事務(wù)攔截器,用戶自定義的后置攔截

創(chuàng)建目標(biāo)代理對(duì)象

afterPropertiesSet 的實(shí)現(xiàn)中有個(gè)針對(duì) transactionAttributeSource 的非空校驗(yàn),那么這個(gè)變量是何時(shí)賦值的呢 ? 還記得使用示例中的關(guān)于事務(wù)屬性的定義 transactionAttributes 嗎 ?
setTransactionAttributes-設(shè)置事務(wù)屬性定義
public void setTransactionAttributes(Properties transactionAttributes) {
    NameMatchTransactionAttributeSource tas = new NameMatchTransactionAttributeSource();
    tas.setProperties(transactionAttributes);
    this.transactionAttributeSource = tas;
}

public void setProperties(Properties transactionAttributes) {
    TransactionAttributeEditor tae = new TransactionAttributeEditor();
    // 遍歷 properties
    for (Iterator it = transactionAttributes.keySet().iterator(); it.hasNext(); ) {
        // key 為匹配的方法名
        String methodName = (String) it.next();
        String value = transactionAttributes.getProperty(methodName);
        // 解析 value
        tae.setAsText(value);
        TransactionAttribute attr = (TransactionAttribute) tae.getValue();
        // 將方法名與事務(wù)屬性定義匹配關(guān)聯(lián)
        addTransactionalMethod(methodName, attr);
    }
}

下面我們就看下 setAsText 方法是如何解析事務(wù)屬性的配置

/**
 * Format is PROPAGATION_NAME,ISOLATION_NAME,readOnly,+Exception1,-Exception2.
 * Null or the empty string means that the method is non transactional.
 * @see java.beans.PropertyEditor#setAsText(java.lang.String)
 */
public void setAsText(String s) throws IllegalArgumentException {
    if (s == null || "".equals(s)) {
        setValue(null);
    }
    else {
        // tokenize it with ","
        // 按 , 分割配置信息
        String[] tokens = StringUtils.commaDelimitedListToStringArray(s);
        RuleBasedTransactionAttribute attr = new RuleBasedTransactionAttribute();

        for (int i = 0; i < tokens.length; i++) {
            String token = tokens[i];
            // 以 PROPAGATION 開(kāi)頭,則配置事務(wù)傳播性
            if (token.startsWith(TransactionDefinition.PROPAGATION_CONSTANT_PREFIX)) {
                attr.setPropagationBehaviorName(tokens[i]);
            }
            // 以 ISOLATION 開(kāi)頭,則配置事務(wù)隔離級(jí)別
            else if (token.startsWith(TransactionDefinition.ISOLATION_CONSTANT_PREFIX)) {
                attr.setIsolationLevelName(tokens[i]);
            }
            // 以 timeout_ 開(kāi)頭,則設(shè)置事務(wù)超時(shí)時(shí)間
            else if (token.startsWith(DefaultTransactionAttribute.TIMEOUT_PREFIX)) {
                String value = token.substring(DefaultTransactionAttribute.TIMEOUT_PREFIX.length());
                attr.setTimeout(Integer.parseInt(value));
            }
            // 若等于 readOnly 則配置事務(wù)只讀
            else if (token.equals(DefaultTransactionAttribute.READ_ONLY_MARKER)) {
                attr.setReadOnly(true);
            }
            // 以 + 開(kāi)頭,則配置哪些異常下不回滾
            else if (token.startsWith(DefaultTransactionAttribute.COMMIT_RULE_PREFIX)) {
                attr.getRollbackRules().add(new NoRollbackRuleAttribute(token.substring(1)));
            }
            // 以 - 開(kāi)頭,則配置哪些異常下回滾
            else if (token.startsWith(DefaultTransactionAttribute.ROLLBACK_RULE_PREFIX)) {
                attr.getRollbackRules().add(new RollbackRuleAttribute(token.substring(1)));
            }
            else {
                throw new IllegalArgumentException("Illegal transaction token: " + token);
            }
        }

        setValue(attr);
    }
}

setAsText 方法的實(shí)現(xiàn)我們就可以搞明白在配置文件中 transactionAttributes 如何配置了,譬如:


    
        PROPAGATION_REQUIRED, ISOLATION_DEFAULT, readOnly
    

也可以這樣配置:


    
        readOnly, ISOLATION_DEFAULT, PROPAGATION_REQUIRED
    

也就是說(shuō) transactionAttributes 的配置只要保證 token 格式正確即可,順序無(wú)關(guān);但是從規(guī)范來(lái)講建議還是保持 PROPAGATION_NAME,ISOLATION_NAME,readOnly,+Exception1,-Exception2. 的格式。

getObject-獲取代理對(duì)象
public Object getObject() {
    // proxy 對(duì)象在 afterPropertiesSet 方法執(zhí)行時(shí)產(chǎn)生
    return this.proxy;
}
代理執(zhí)行
是否支持事務(wù)

重拾-Spring AOP 中我們知道,當(dāng)代理對(duì)象在執(zhí)行的時(shí)候會(huì)先獲取當(dāng)前方法所匹配的 advisor (參見(jiàn)類 JdkDynamicAopProxy); 而 TransactionProxyFactoryBean 在創(chuàng)建代理對(duì)象的時(shí)候會(huì)將 TransactionInterceptor 綁定到 TransactionAttributeSourceAdvisor 上,那么我就看下 TransactionAttributeSourceAdvisor 是如何匹配方法的。

public class TransactionAttributeSourceAdvisor extends StaticMethodMatcherPointcutAdvisor {

    private TransactionAttributeSource transactionAttributeSource;

    public TransactionAttributeSourceAdvisor(TransactionInterceptor ti) {
        super(ti);
        if (ti.getTransactionAttributeSource() == null) {
            throw new AopConfigException("Cannot construct a TransactionAttributeSourceAdvisor using a " +
                                                                     "TransactionInterceptor that has no TransactionAttributeSource configured");
        }
        this.transactionAttributeSource = ti.getTransactionAttributeSource();
    }

    public boolean matches(Method m, Class targetClass) {
        return (this.transactionAttributeSource.getTransactionAttribute(m, targetClass) != null);
    }
}

TransactionAttributeSourceAdvisor 判斷方法是否匹配時(shí),實(shí)際是由 NameMatchTransactionAttributeSource 的方法 getTransactionAttribute 來(lái)處理。

public TransactionAttribute getTransactionAttribute(Method method, Class targetClass) {
    // 獲取目標(biāo)方法名
    String methodName = method.getName();
    // 獲取目標(biāo)方法匹配的事務(wù)屬性定義
    TransactionAttribute attr = (TransactionAttribute) this.nameMap.get(methodName);
    // 如果 attr 不為空說(shuō)明當(dāng)前方法配置了事務(wù)屬性定義,也就是當(dāng)前方法需要事務(wù)管理
    if (attr != null) {
        return attr;
    }
    else {
        // look up most specific name match
        String bestNameMatch = null;
        for (Iterator it = this.nameMap.keySet().iterator(); it.hasNext();) {
            // 判斷當(dāng)前方法是否匹配通配符的方式
            String mappedName = (String) it.next();
            if (isMatch(methodName, mappedName) &&
                    (bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
                attr = (TransactionAttribute) this.nameMap.get(mappedName);
                bestNameMatch = mappedName;
            }
        }
        return attr;
    }
}

protected boolean isMatch(String methodName, String mappedName) {
    return (mappedName.endsWith("*") && methodName.startsWith(mappedName.substring(0, mappedName.length() - 1))) ||
            (mappedName.startsWith("*") && methodName.endsWith(mappedName.substring(1, mappedName.length())));
}
TransactionInterceptor-事務(wù)攔截

在完成判斷當(dāng)前方法是否需要事務(wù)管理后,如果需要事務(wù)管理最終會(huì)調(diào)用 TransactionInterceptor 執(zhí)行事務(wù)攔截的處理。

public final Object invoke(MethodInvocation invocation) throws Throwable {
    Class targetClass = (invocation.getThis() != null) ? invocation.getThis().getClass() : null;
    // if the transaction attribute is null, the method is non-transactional
    // 獲取當(dāng)前方法所支持的事務(wù)配置屬性,若不存在則說(shuō)明當(dāng)前方法不需要事務(wù)管理
    TransactionAttribute transAtt = this.transactionAttributeSource.getTransactionAttribute(invocation.getMethod(), targetClass);
    TransactionStatus status = null;
    TransactionStatus oldTransactionStatus = null;
    
    // create transaction if necessary
    if (transAtt != null) {        
        // the transaction manager will flag an error if an incompatible tx already exists
        // 通過(guò)事務(wù)管理獲取事務(wù),該事務(wù)可能是新創(chuàng)建的也可能是當(dāng)前上下文已存在的事務(wù)
        // 返回事務(wù)狀態(tài)
        status = this.transactionManager.getTransaction(transAtt);
        
        // make the TransactionStatus available to callees
        oldTransactionStatus = (TransactionStatus) currentTransactionStatus.get();
        currentTransactionStatus.set(status);
    }
    else {
        // it isn"t a transactional method
        
    }

    Object retVal = null;
    try {
        // 目標(biāo)方法執(zhí)行
        retVal = invocation.proceed();
    }
    catch (Throwable ex) {
        // target invocation exception
        if (status != null) {
            // 異常處理 可能會(huì)執(zhí)行事務(wù)的回滾
            onThrowable(invocation, transAtt, status, ex);
        }
        throw ex;
    }
    finally {
        if (transAtt != null) {
            // use stack to restore old transaction status if one was set
            currentTransactionStatus.set(oldTransactionStatus);
        }
    }
    if (status != null) {
        // 通過(guò)事務(wù)管理執(zhí)行事務(wù)提交
        this.transactionManager.commit(status);
    }
    return retVal;
}
private void onThrowable(MethodInvocation invocation, TransactionAttribute txAtt,
                             TransactionStatus status, Throwable ex) {
    // 判斷異常是否需要回滾
    if (txAtt.rollbackOn(ex)) {
        try {
            // 通過(guò)事務(wù)管理執(zhí)行回滾
            this.transactionManager.rollback(status);
        }
        catch (TransactionException tex) {
            logger.error("Application exception overridden by rollback exception", ex);
            throw tex;
        }
    }
    else {
        // Will still roll back if rollbackOnly is true
        // 異常不需要回滾的話 則提交事務(wù)
        this.transactionManager.commit(status);
    }
}

TransactionInterceptor 的處理邏輯來(lái)看,我們知道其主要做以下事情:

獲取當(dāng)前方法所定義的事務(wù)屬性

通過(guò)事務(wù)管理器 Transaction Manager 來(lái)獲取事務(wù)

目標(biāo)方法執(zhí)行

執(zhí)行異常處理,如異常需要回滾則通過(guò)事務(wù)管理器執(zhí)行事務(wù) rollback,反之執(zhí)行事務(wù) commit

方法執(zhí)行成功則執(zhí)行事務(wù) commit

也就是說(shuō) TransactionInterceptor (事務(wù)攔截器) 主要是將事務(wù)相關(guān)的動(dòng)作委托給 TransactionManager (事務(wù)管理器)處理
TransactionManager-事務(wù)管理
本文是以 DataSourceTransactionManager 為例來(lái)分析事務(wù)的管理實(shí)現(xiàn)
getTransaction-獲取事務(wù)
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
    // 獲取事務(wù)
    Object transaction = doGetTransaction();
    if (definition == null) {
        // 若 definition == null 則采用默認(rèn)的事務(wù)定義
        definition = new DefaultTransactionDefinition();
    }

    // 判斷當(dāng)前上下文是否開(kāi)啟過(guò)事務(wù)
    if (isExistingTransaction(transaction)) {
        // 當(dāng)前上下文開(kāi)啟過(guò)事務(wù)
        // 如果當(dāng)前方法匹配的事務(wù)傳播性為 PROPAGATION_NEVER 說(shuō)明不需要事務(wù)則拋出異常
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
            throw new IllegalTransactionStateException("Transaction propagation "never" but existing transaction found");
        }

        // 如果當(dāng)前方法匹配的事務(wù)傳播性為 PROPAGATION_NOT_SUPPORTED 說(shuō)明該方法不應(yīng)該運(yùn)行在事務(wù)中,則將當(dāng)前事務(wù)掛起
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
            // 將當(dāng)前事務(wù)掛起
            Object suspendedResources = suspend(transaction);
            boolean newSynchronization = (this.transactionSynchronization == SYNCHRONIZATION_ALWAYS);
            // 返回的事務(wù)狀態(tài)為 不需要事務(wù)
            return newTransactionStatus(null, false, newSynchronization,
                                        definition.isReadOnly(), debugEnabled, suspendedResources);
        }
        // 如果當(dāng)前方法匹配的事務(wù)傳播性為 PROPAGATION_REQUIRES_NEW 表示當(dāng)前方法必須運(yùn)行在它自己的事務(wù)中;將已存在的事務(wù)掛起,重新開(kāi)啟事務(wù)
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
            if (debugEnabled) {
                logger.debug("Creating new transaction, suspending current one");
            }
            // 掛起當(dāng)前事務(wù)
            Object suspendedResources = suspend(transaction);
            // 重新開(kāi)啟個(gè)事務(wù)
            doBegin(transaction, definition);
            boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);
            // 返回的事務(wù)狀態(tài)為 新建事務(wù)
            return newTransactionStatus(transaction, true, newSynchronization,
                                        definition.isReadOnly(), debugEnabled, suspendedResources);
        }
        else {
            boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);
            // 其他的傳播行為 表示在已存在的事務(wù)中執(zhí)行
            return newTransactionStatus(transaction, false, newSynchronization,
                                        definition.isReadOnly(), debugEnabled, null);
        }
    }

    if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
        throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
    }

    // 如果傳播性為 PROPAGATION_MANDATORY 說(shuō)明必須在事務(wù)中執(zhí)行,若當(dāng)前沒(méi)有事務(wù)的話則拋出異常
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
        throw new IllegalTransactionStateException("Transaction propagation "mandatory" but no existing transaction found");
    }

    // 當(dāng)前上下文不存在事務(wù)
    // 若傳播性為 PROPAGATION_REQUIRED 或 PROPAGATION_REQUIRES_NEW 則開(kāi)啟新的事務(wù)執(zhí)行
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
        definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
        // 開(kāi)啟新的 connection 并取消自動(dòng)提交,將 connection 綁定當(dāng)前線程
        doBegin(transaction, definition);
        boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);
        return newTransactionStatus(transaction, true, newSynchronization,
                                    definition.isReadOnly(), debugEnabled, null);
    }
    else {
        // "empty" (-> no) transaction
        boolean newSynchronization = (this.transactionSynchronization == SYNCHRONIZATION_ALWAYS);
        // 返回事務(wù)狀態(tài)為 不需要事務(wù)
        return newTransactionStatus(null, false, newSynchronization,
                                    definition.isReadOnly(), debugEnabled, null);
    }
}

protected Object doGetTransaction() {
    // 判斷當(dāng)前線程是否開(kāi)啟過(guò)事務(wù)
    if (TransactionSynchronizationManager.hasResource(this.dataSource)) {
        // 獲取當(dāng)前已存在的 connectoin holder
        ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
        return new DataSourceTransactionObject(holder);
    }
    else {
        return new DataSourceTransactionObject();
    }
}

看到了這里,是不是突然明白 PROPAGATION (事務(wù)傳播性) 是干什么的了;

簡(jiǎn)單來(lái)說(shuō), PROPAGATION 就是為了告訴 Spring 當(dāng)前方法需不需要事務(wù),是在已存在的事務(wù)中執(zhí)行,還是新開(kāi)啟事務(wù)執(zhí)行;也可以認(rèn)為是繼承上個(gè)方法棧的事務(wù),還是擁有自己的事務(wù)。

TransactionManager 獲取事務(wù)的過(guò)程實(shí)際就是通過(guò)當(dāng)前方法定義的 PROPAGATION (事務(wù)傳播性) 和當(dāng)前上下文是否存在事務(wù)來(lái)判斷是否需要事務(wù),是否需要開(kāi)啟新的事務(wù)或者是使用當(dāng)前已存在的事務(wù)。

下面看下如何開(kāi)啟新的事務(wù) doBegin

protected void doBegin(Object transaction, TransactionDefinition definition) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;

    // cache to avoid repeated checks
    boolean debugEnabled = logger.isDebugEnabled();

    // 判斷 connection holder 是否為空
    // 兩種場(chǎng)景下可能為空:
    // 1. 上下文不存在事務(wù)的時(shí)候
    // 2. 上下文已存在的事務(wù)被掛起的時(shí)候
    if (txObject.getConnectionHolder() == null) {
        if (debugEnabled) {
            logger.debug("Opening new connection for JDBC transaction");
        }
        // 開(kāi)啟新的 connection
        Connection con = DataSourceUtils.getConnection(this.dataSource, false);
        txObject.setConnectionHolder(new ConnectionHolder(con));
    }

    Connection con = txObject.getConnectionHolder().getConnection();
    try {
        // apply read-only
        if (definition.isReadOnly()) {
            try {
                // 如果定義了只讀,設(shè)置 connection 為只讀
                con.setReadOnly(true);
            }
            catch (Exception ex) {
                // SQLException or UnsupportedOperationException
                logger.warn("Could not set JDBC connection read-only", ex);
            }
        }

        // apply isolation level
        // 設(shè)置事務(wù)隔離級(jí)別
        if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
            txObject.setPreviousIsolationLevel(new Integer(con.getTransactionIsolation()));
            con.setTransactionIsolation(definition.getIsolationLevel());
        }

        // 若 connection 為自動(dòng)提交則取消
        if (con.getAutoCommit()) {
            txObject.setMustRestoreAutoCommit(true);
            con.setAutoCommit(false);
        }

        // 設(shè)置超時(shí)時(shí)間
        if (definition.getTimeout() != TransactionDefinition.TIMEOUT_DEFAULT) {
            txObject.getConnectionHolder().setTimeoutInSeconds(definition.getTimeout());
        }

        // 將當(dāng)前 connection holder 綁定到當(dāng)前上下文
        TransactionSynchronizationManager.bindResource(this.dataSource, txObject.getConnectionHolder());
    }
    catch (SQLException ex) {
        throw new CannotCreateTransactionException("Could not configure connection", ex);
    }
}

doBegin 執(zhí)行開(kāi)啟事務(wù)的操作,在上下文不存在事務(wù)或者上下文事務(wù)被掛起的時(shí)候會(huì)新打開(kāi)一個(gè) connection, 并按照事務(wù)定義設(shè)置相關(guān)屬性,譬如是否只讀,取消自動(dòng)提交,設(shè)置事務(wù)隔離級(jí)別,設(shè)置超時(shí)時(shí)間;最后會(huì)將 connection 綁定到當(dāng)前上下文,也即當(dāng)前線程。

doSuspend-事務(wù)掛起
protected Object doSuspend(Object transaction) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
    // 將當(dāng)前事務(wù)的 connection holder 置為空
    txObject.setConnectionHolder(null);
    // 并將當(dāng)前事務(wù)與上下文解綁
    return TransactionSynchronizationManager.unbindResource(this.dataSource);
}

事務(wù)掛起既是將當(dāng)前事務(wù)的連接持有者清空并與當(dāng)前上下文解綁,保證后續(xù)能夠重新開(kāi)啟事務(wù)。

數(shù)據(jù)庫(kù)操作
針對(duì)數(shù)據(jù)庫(kù)的操作,本文以 Spring 提供的 jdbcTemplate 工具類進(jìn)行分析。
public Object execute(final StatementCallback action) {
    // 若當(dāng)前需要事務(wù)管理的話,那么此時(shí)獲取的 connection 則是 transaction manager bind 的 connection
    // 這樣就保證數(shù)據(jù)庫(kù)操作的時(shí)候所獲得的的 connection 與 事務(wù)管理的一致
    Connection con = DataSourceUtils.getConnection(getDataSource());
    Statement stmt = null;
    // 以下代碼省略 此處重點(diǎn)關(guān)注如何獲取 connection
}
public static Connection getConnection(DataSource ds, boolean allowSynchronization)
        throws CannotGetJdbcConnectionException {
    // 從當(dāng)前上下文獲取 connection holder
    ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(ds);
    if (conHolder != null) {
        return conHolder.getConnection();
    }
    else {
        try {
            // 反之新打開(kāi)一個(gè) connection
            Connection con = ds.getConnection();
            if (allowSynchronization && TransactionSynchronizationManager.isSynchronizationActive()) {
                logger.debug("Registering transaction synchronization for JDBC connection");
                // use same Connection for further JDBC actions within the transaction
                // thread object will get removed by synchronization at transaction completion
                conHolder = new ConnectionHolder(con);
                TransactionSynchronizationManager.bindResource(ds, conHolder);
                TransactionSynchronizationManager.registerSynchronization(new ConnectionSynchronization(conHolder, ds));
            }
            return con;
        }
        catch (SQLException ex) {
            throw new CannotGetJdbcConnectionException("Could not get JDBC connection", ex);
        }
    }
}

從上述代碼我們可以看到,當(dāng)通過(guò) jdbcTemplate 操作數(shù)據(jù)庫(kù)時(shí)會(huì)先從當(dāng)前上下文中獲取 connection; 這樣就保證了所獲取的事務(wù)與事務(wù)攔截器的事務(wù)為同一個(gè)實(shí)例,也就是將事務(wù)交給了 Spring 來(lái)管理。

commit-事務(wù)提交
public final void commit(TransactionStatus status) throws TransactionException {
    DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
    // 省略
    else {
        try {
            try {
                triggerBeforeCommit(defStatus);
                triggerBeforeCompletion(defStatus);
                if (status.isNewTransaction()) {
                    logger.info("Initiating transaction commit");
                    // 執(zhí)行事務(wù)提交
                    doCommit(defStatus);
                }
            }
            // 省略
        }
        finally {
            cleanupAfterCompletion(defStatus);
        }
    }
}

doCommit 執(zhí)行事務(wù)提交

protected void doCommit(DefaultTransactionStatus status) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
    if (status.isDebug()) {
        logger.debug("Committing JDBC transaction [" + txObject.getConnectionHolder().getConnection() + "]");
    }
    try {
        // 事務(wù)提交
        txObject.getConnectionHolder().getConnection().commit();
    }
    catch (SQLException ex) {
        throw new TransactionSystemException("Could not commit", ex);
    }
}
resume-事務(wù)恢復(fù)

從上文的 commit 事務(wù)提交操作發(fā)現(xiàn),在完成事務(wù)提交之后,還有個(gè)后置動(dòng)作 cleanupAfterCompletion, 該方法會(huì)對(duì)掛起中的事務(wù)執(zhí)行恢復(fù)操作。

private void cleanupAfterCompletion(DefaultTransactionStatus status) {
    if (status.isNewSynchronization()) {
        TransactionSynchronizationManager.clearSynchronization();
    }
    if (status.isNewTransaction()) {
        doCleanupAfterCompletion(status.getTransaction());
    }
    // 當(dāng)存在掛起的事務(wù)時(shí),執(zhí)行恢復(fù)掛起的事務(wù)
    if (status.getSuspendedResources() != null) {
        if (status.isDebug()) {
            logger.debug("Resuming suspended transaction");
        }
        resume(status.getTransaction(), status.getSuspendedResources());
    }
}
protected void doResume(Object transaction, Object suspendedResources) {
    // 將掛起的事務(wù)綁定的 connection 重新綁定到當(dāng)前上下文
    ConnectionHolder conHolder = (ConnectionHolder) suspendedResources;
    TransactionSynchronizationManager.bindResource(this.dataSource, conHolder);
}

事務(wù)的 resume 就是將掛起的事務(wù)重新綁定到當(dāng)前上下文中。

rollback-事務(wù)回滾

當(dāng) TransactionInterceptor 調(diào)用目標(biāo)方法執(zhí)行出現(xiàn)異常的時(shí)候,會(huì)進(jìn)行異常處理執(zhí)行方法 onThrowable

private void onThrowable(MethodInvocation invocation, TransactionAttribute txAtt,
                         TransactionStatus status, Throwable ex) {
    if (txAtt.rollbackOn(ex)) {
        try {
            // 異常需要回滾
            this.transactionManager.rollback(status);
        }
        catch (TransactionException tex) {
            throw tex;
        }
    }
    else {
        // 異常不需要回滾的話 則提交事務(wù)
        this.transactionManager.commit(status);
    }
}

onThrowable 方法會(huì)通過(guò)配置判斷當(dāng)前異常是否需要回滾。

public final void rollback(TransactionStatus status) throws TransactionException {
    DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
    try {
        try {
            triggerBeforeCompletion(defStatus);
            if (status.isNewTransaction()) {
                // 執(zhí)行事務(wù)回滾
                logger.info("Initiating transaction rollback");
                doRollback(defStatus);
            }
            else if (defStatus.getTransaction() != null) {
                if (defStatus.isDebug()) {
                    logger.debug("Setting existing transaction rollback-only");
                }
                doSetRollbackOnly(defStatus);
            }
            else {
                logger.info("Should roll back transaction but cannot - no transaction available");
            }
        }
        catch (TransactionException ex) {
            triggerAfterCompletion(defStatus, TransactionSynchronization.STATUS_UNKNOWN, ex);
            throw ex;
        }
        triggerAfterCompletion(defStatus, TransactionSynchronization.STATUS_ROLLED_BACK, null);
    }
    finally {
        cleanupAfterCompletion(defStatus);
    }
}

protected void doRollback(DefaultTransactionStatus status) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
    try {
        // 執(zhí)行回滾
        txObject.getConnectionHolder().getConnection().rollback();
    }
    catch (SQLException ex) {
        throw new TransactionSystemException("Could not rollback", ex);
    }
}
小結(jié)

此時(shí)我們基本明白了 Spring Transaction 的實(shí)現(xiàn)原理,下面對(duì)其實(shí)現(xiàn)做個(gè)小結(jié):

Spring Transaction 是基于 Spring AOP 的一種實(shí)現(xiàn)

Spring Transaction 通過(guò)配置創(chuàng)建事務(wù) advisor 并創(chuàng)建目標(biāo)對(duì)象代理類

目標(biāo)方法執(zhí)行時(shí)將會(huì)被 TransactionInterceptor 攔截

TransactionInterceptor 會(huì)委派 TransactionManager 執(zhí)行事務(wù)的創(chuàng)建,事務(wù)提交,事務(wù)回滾的動(dòng)作

TransactionManager 會(huì)根據(jù)當(dāng)前方法配置的事務(wù)傳播性及當(dāng)前上下文是否存在事務(wù)來(lái)判斷是否新建事務(wù)

TransactionManager 當(dāng)新建事務(wù)時(shí)會(huì)將事務(wù)綁定到當(dāng)前上下文,以保證目標(biāo)方法執(zhí)行時(shí)獲取的事務(wù)為同一實(shí)例

TransactionManager 執(zhí)行事務(wù)掛起時(shí)會(huì)將當(dāng)前事務(wù)與當(dāng)前上下文解除綁定關(guān)系

TransactionManager 執(zhí)行事務(wù)恢復(fù)時(shí)會(huì)將已掛起的事務(wù)重新與當(dāng)前上下文綁定

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/73618.html

相關(guān)文章

  • 架構(gòu)~微服務(wù)

    摘要:接下來(lái)繼續(xù)介紹三種架構(gòu)模式,分別是查詢分離模式微服務(wù)模式多級(jí)緩存模式。分布式應(yīng)用程序可以基于實(shí)現(xiàn)諸如數(shù)據(jù)發(fā)布訂閱負(fù)載均衡命名服務(wù)分布式協(xié)調(diào)通知集群管理選舉分布式鎖和分布式隊(duì)列等功能。 SpringCloud 分布式配置 SpringCloud 分布式配置 史上最簡(jiǎn)單的 SpringCloud 教程 | 第九篇: 服務(wù)鏈路追蹤 (Spring Cloud Sleuth) 史上最簡(jiǎn)單的 S...

    xinhaip 評(píng)論0 收藏0
  • 架構(gòu)~微服務(wù) - 收藏集 - 掘金

    摘要:它就是史上最簡(jiǎn)單的教程第三篇服務(wù)消費(fèi)者后端掘金上一篇文章,講述了通過(guò)去消費(fèi)服務(wù),這篇文章主要講述通過(guò)去消費(fèi)服務(wù)。概覽和架構(gòu)設(shè)計(jì)掘金技術(shù)征文后端掘金是基于的一整套實(shí)現(xiàn)微服務(wù)的框架。 Spring Boot 配置文件 – 在坑中實(shí)踐 - 后端 - 掘金作者:泥瓦匠鏈接:Spring Boot 配置文件 – 在坑中實(shí)踐版權(quán)歸作者所有,轉(zhuǎn)載請(qǐng)注明出處本文提綱一、自動(dòng)配置二、自定義屬性三、ran...

    church 評(píng)論0 收藏0
  • 重拾-Spring AOP-自動(dòng)代理

    摘要:是通過(guò)判斷當(dāng)前是否匹配,只有匹配的才會(huì)創(chuàng)建代理。實(shí)現(xiàn)分析類結(jié)構(gòu)從上圖類結(jié)構(gòu),我們知道其實(shí)現(xiàn)與類似都是通過(guò)實(shí)現(xiàn)接口在完成實(shí)例化后進(jìn)行自動(dòng)代理處理。 概述 在上一篇 重拾-Spring AOP 中我們會(huì)發(fā)現(xiàn) Spring AOP 是通過(guò)類 ProxyFactoryBean 創(chuàng)建代理對(duì)象,其有個(gè)缺陷就是只能代理一個(gè)目標(biāo)對(duì)象 bean, 當(dāng)代理目標(biāo)類過(guò)多時(shí),配置文件臃腫不方便管理維護(hù),因此 S...

    Mr_houzi 評(píng)論0 收藏0
  • 后臺(tái) - 收藏集 - 掘金

    摘要:探究系統(tǒng)登錄驗(yàn)證碼的實(shí)現(xiàn)后端掘金驗(yàn)證碼生成類手把手教程后端博客系統(tǒng)第一章掘金轉(zhuǎn)眼間時(shí)間就從月份到現(xiàn)在的十一月份了。提供了與標(biāo)準(zhǔn)不同的工作方式我的后端書(shū)架后端掘金我的后端書(shū)架月前本書(shū)架主要針對(duì)后端開(kāi)發(fā)與架構(gòu)。 Spring Boot干貨系列總綱 | 掘金技術(shù)征文 - 掘金原本地址:Spring Boot干貨系列總綱博客地址:http://tengj.top/ 前言 博主16年認(rèn)識(shí)Spin...

    CrazyCodes 評(píng)論0 收藏0
  • Spring Security

    摘要:框架具有輕便,開(kāi)源的優(yōu)點(diǎn),所以本譯見(jiàn)構(gòu)建用戶管理微服務(wù)五使用令牌和來(lái)實(shí)現(xiàn)身份驗(yàn)證往期譯見(jiàn)系列文章在賬號(hào)分享中持續(xù)連載,敬請(qǐng)查看在往期譯見(jiàn)系列的文章中,我們已經(jīng)建立了業(yè)務(wù)邏輯數(shù)據(jù)訪問(wèn)層和前端控制器但是忽略了對(duì)身份進(jìn)行驗(yàn)證。 重拾后端之Spring Boot(四):使用JWT和Spring Security保護(hù)REST API 重拾后端之Spring Boot(一):REST API的搭建...

    keelii 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<