欧美1区2区3区激情无套,两个女人互添下身视频在线观看,久久av无码精品人妻系列,久久精品噜噜噜成人,末发育娇小性色xxxx

Spring源碼的圖文詳解:Spring事務(wù)

一:概述及目錄

下?我會簡單介紹?下 Spring 事務(wù)的基礎(chǔ)知識,以及使??法,然后直接對源碼進(jìn)?拆解。

目錄:

二. 項?準(zhǔn)備

下?是 DB 數(shù)據(jù)和 DB 操作接?:

Id

uname

usex

1

小王

2

小李

1

小趙

@Data
public class MyUser {
    private int id;
    private String uname;
    private String usex;
}
復(fù)制代碼
public interface UserDao {
    // select * from user_test where id = "#{id}"
     MyUser selectUserById(Integer uid);
    // update user_test set uname =#{uname},usex = #{usex} where id = #{id}
     int updateUser(MyUser user);
}
復(fù)制代碼

基礎(chǔ)測試代碼,testSuccess() 是事務(wù)?效的情況:

@Service
public class Model {
    @Autowired
    private UserDao userDao;
    public void update(Integer id) {
        MyUser user = new MyUser();
        user.setId(id);
        user.setUname("張三-testing");
        user.setUsex("?");
        userDao.updateUser(user);
    }
    public MyUser query(Integer id) {
        MyUser user = userDao.selectUserById(id);
        return user;
    }
    // 正常情況
    @Transactional(rollbackFor = Exception.class)
    public void testSuccess() throws Exception {
        Integer id = 1;
        MyUser user = query(id);
        System.out.println("原記錄:" + user);
        update(id);
        throw new Exception("事務(wù)?效");
    }
}
復(fù)制代碼

執(zhí)???:

public class SpringMyBatisTest {
    public static void main(String[] args) throws Exception {
        String xmlPath = "applicationContext.xml";
        ApplicationContext applicationContext = new
                ClassPathXmlApplicationContext(xmlPath);
        Model uc = (Model) applicationContext.getBean("model");
        uc.testSuccess();
    }
}
復(fù)制代碼

輸出:

三:Spring 事務(wù)?作流程

為了?便?家能更好看懂后?的源碼,我先整體介紹?下源碼的執(zhí)?流程,讓?家有?個整體的認(rèn)識,否則容易被 繞進(jìn)去。

整個 Spring 事務(wù)源碼,其實分為 2 塊,我們會結(jié)合上?的示例,給?家進(jìn)?講解。

第?塊是后置處理,我們在創(chuàng)建 Model Bean 的后置處理器中,??會做兩件事情:

獲取 Model 的切??法:?先會拿到所有的切?信息,和 Model 的所有?法進(jìn)?匹配,然后找到 Model 所有需 要進(jìn)?事務(wù)處理的?法,匹配成功的?法,還需要將事務(wù)屬性保存到緩存 attributeCache 中。

創(chuàng)建 AOP 代理對象:結(jié)合 Model 需要進(jìn)? AOP 的?法,選擇 Cglib 或 JDK,創(chuàng)建 AOP 代理對象。

第?塊是事務(wù)執(zhí)?,整個邏輯?較復(fù)雜,我只選取 4 塊最核?的邏輯,分別為從緩存拿到事務(wù)屬性、創(chuàng)建并開啟事 務(wù)、執(zhí)?業(yè)務(wù)邏輯、提交或者回滾事務(wù)。

四. 源碼解讀

注意:Spring 的版本是 5.2.15.RELEASE,否則和我的代碼不?樣?。?!

上?的知識都不難,下?才是我們的重頭戲,讓我們一起??遍代碼流程。

4.1 代碼??

這?需要多跑?次,把前?的 beanName 跳過去,只看 model。

進(jìn)? doGetBean(),進(jìn)?創(chuàng)建 Bean 的邏輯。

進(jìn)? createBean(),調(diào)? doCreateBean()。

進(jìn)? doCreateBean(),調(diào)? initializeBean()。

如果看過我前??期系列源碼的同學(xué),對這個??應(yīng)該會?常熟悉,其實就是?來創(chuàng)建代理對象。

4.2 創(chuàng)建代理對象

這?是重點!敲?板?。?!

先獲取 model 類的所有切?列表; 創(chuàng)建?個 AOP 的代理對象。

4.2.1 獲取切?列表

這?有 2 個重要的?法,先執(zhí)? findCandidateAdvisors(),待會我們還會再返回 findEligibleAdvisors()。

依次返回,重新來到 findEligibleAdvisors()。

進(jìn)? canApply(),開始匹配 model 的切?。

這?是重點!敲?板!?。?/strong> 這?只會匹配到 Model.testSuccess() ?法,我們直接進(jìn)?匹配邏輯。

如果匹配成功,還會把事務(wù)的屬性配置信息放? attributeCache 緩存。

我們依次返回到 getTransactionAttribute(),再看看放?緩存中的數(shù)據(jù)。

再回到該?節(jié)開頭,我們拿到 mdoel 的切?信息,去創(chuàng)建 AOP 代理對象。

4.2.2 創(chuàng)建 AOP 代理對象

創(chuàng)建 AOP 代理對象的邏輯,在上?篇?章【Spring源碼解析-Spring AOP】講解過,我是通過 Cglib 創(chuàng)建,感興趣的同學(xué)可以翻?下我的歷史?章。

4.3 事務(wù)執(zhí)?

回到業(yè)務(wù)邏輯,通過 model 的 AOP 代理對象,開始執(zhí)?主?法。

因為代理對象是 Cglib ?式創(chuàng)建,所以通過 Cglib 來執(zhí)?。

這?是重點!敲?板?。。?/strong>

下?的代碼是事務(wù)執(zhí)?的核?邏輯 invokeWithinTransaction()。

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
                                         final InvocationCallback invocation) throws Throwable {
    //獲取我們的事務(wù)屬源對象
    TransactionAttributeSource tas = getTransactionAttributeSource();
    //通過事務(wù)屬性源對象獲取到我們的事務(wù)屬性信息
    final TransactionAttribute txAttr = (tas != null ?
            tas.getTransactionAttribute(method, targetClass) : null);
    //獲取我們配置的事務(wù)管理器對象
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    //從tx屬性對象中獲取出標(biāo)注了@Transactionl的?法描述符
    final String joinpointIdentification = methodIdentification(method,
            targetClass, txAttr);
    //處理聲明式事務(wù)
    if (txAttr == null || !(tm instanceof
            CallbackPreferringPlatformTransactionManager)) {
        //有沒有必要創(chuàng)建事務(wù)
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr,
                joinpointIdentification);
        Object retVal;
        try {
            //調(diào)?鉤?函數(shù)進(jìn)?回調(diào)?標(biāo)?法
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            //拋出異常進(jìn)?回滾處理
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            //清空我們的線程變量中transactionInfo的值
            cleanupTransactionInfo(txInfo);
        }
        //提交事務(wù)
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }
    //編程式事務(wù)
    else {
        // 這?不是我們的重點,省略...
    }
}
復(fù)制代碼

4.3.1 獲取事務(wù)屬性

在 invokeWithinTransaction() 中,我們找到獲取事務(wù)屬性的??。

從 attributeCache 獲取事務(wù)的緩存數(shù)據(jù),緩存數(shù)據(jù)是在 “3.2.1 獲取切?列表” 中保存的。

4.3.2 創(chuàng)建事務(wù)

通過 doGetTransaction() 獲取事務(wù)。

protected Object doGetTransaction() {
    //創(chuàng)建?個數(shù)據(jù)源事務(wù)對象
    DataSourceTransactionObject txObject = new DataSourceTransactionObject();
    //是否允許當(dāng)前事務(wù)設(shè)置保持點
    txObject.setSavepointAllowed(isNestedTransactionAllowed());
    /**
     * TransactionSynchronizationManager 事務(wù)同步管理器對象(該類中都是局部線程變量)
     * ?來保存當(dāng)前事務(wù)的信息,我們第?次從這?去線程變量中獲取 事務(wù)連接持有器對象 通過數(shù)據(jù)源為key
     去獲取
     * 由于第?次進(jìn)來開始事務(wù) 我們的事務(wù)同步管理器中沒有被存放.所以此時獲取出來的conHolder為null
     */
    ConnectionHolder conHolder =
            (ConnectionHolder)
                    TransactionSynchronizationManager.getResource(obtainDataSource());
    txObject.setConnectionHolder(conHolder, false);
    //返回事務(wù)對象
    return txObject;
}
復(fù)制代碼

通過 startTransaction() 開啟事務(wù)。

下?是開啟事務(wù)的詳細(xì)邏輯,了解?下即可。

protected void doBegin(Object transaction, TransactionDefinition definition) {
    //強制轉(zhuǎn)化事務(wù)對象
    DataSourceTransactionObject txObject = (DataSourceTransactionObject)
            transaction;
    Connection con = null;
    try {
        //判斷事務(wù)對象沒有數(shù)據(jù)庫連接持有器
        if (!txObject.hasConnectionHolder() ||
                txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
            //通過數(shù)據(jù)源獲取?個數(shù)據(jù)庫連接對象
            Connection newCon = obtainDataSource().getConnection();
            if (logger.isDebugEnabled()) {
                logger.debug("Acquired Connection [" + newCon + "] for JDBC
                        transaction");
            }
            //把我們的數(shù)據(jù)庫連接包裝成?個ConnectionHolder對象 然后設(shè)置到我們的txObject對象
            中去
            txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
        }
        //標(biāo)記當(dāng)前的連接是?個同步事務(wù)
        txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
        con = txObject.getConnectionHolder().getConnection();
        //為當(dāng)前的事務(wù)設(shè)置隔離級別
        Integer previousIsolationLevel =
                DataSourceUtils.prepareConnectionForTransaction(con, definition);
        txObject.setPreviousIsolationLevel(previousIsolationLevel);
        最后返回到 invokeWithinTransaction(),得到 txInfo 對象。
        //關(guān)閉?動提交
        if (con.getAutoCommit()) {
            txObject.setMustRestoreAutoCommit(true);
            if (logger.isDebugEnabled()) {
                logger.debug("Switching JDBC Connection [" + con + "] to manual
                        commit");
            }
            con.setAutoCommit(false);
        }
        //判斷事務(wù)為只讀事務(wù)
        prepareTransactionalConnection(con, definition);
        //設(shè)置事務(wù)激活
        txObject.getConnectionHolder().setTransactionActive(true);
        //設(shè)置事務(wù)超時時間
        int timeout = determineTimeout(definition);
        if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
            txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
        }
        // 綁定我們的數(shù)據(jù)源和連接到我們的同步管理器上 把數(shù)據(jù)源作為key,數(shù)據(jù)庫連接作為value 設(shè)
        置到線程變量中
        if (txObject.isNewConnectionHolder()) {
            TransactionSynchronizationManager.bindResource(obtainDataSource(),
                    txObject.getConnectionHolder());
        }
    }
    catch (Throwable ex) {
        if (txObject.isNewConnectionHolder()) {
            //釋放數(shù)據(jù)庫連接
            DataSourceUtils.releaseConnection(con, obtainDataSource());
            txObject.setConnectionHolder(null, false);
        }
        throw new CannotCreateTransactionException("Could not open JDBC Connection
       
    }
}
復(fù)制代碼

最后返回到 invokeWithinTransaction(),得到 txInfo 對象。

4.3.3 執(zhí)?邏輯

還是在 invokeWithinTransaction() 中,開始執(zhí)?業(yè)務(wù)邏輯。

進(jìn)?到真正的業(yè)務(wù)邏輯。

執(zhí)?完畢后拋出異常,依次返回,?后續(xù)的回滾事務(wù)邏輯。

4.3.4 回滾事務(wù)

還是在 invokeWithinTransaction() 中,進(jìn)?回滾事務(wù)的邏輯。

執(zhí)?回滾邏輯很簡單,我們只看如何判斷是否回滾。

如果拋出的異常類型,和事務(wù)定義的異常類型匹配,證明該異常需要捕獲。

之所以?遞歸,不僅需要判斷拋出異常的本身,還需要判斷它繼承的?類異常,滿?任意?個即可捕獲。

到這?,所有的流程結(jié)束。

五. 總要有總結(jié)

我們再?節(jié)?下,?章先介紹了事務(wù)的使?示例,以及事務(wù)的執(zhí)?流程。

之后再剖析了事務(wù)的源碼,分為 2 塊:

先匹配出 model 對象所有關(guān)于事務(wù)的切?列表,并將匹配成功的事務(wù)屬性保存到緩存; 從緩存取出事務(wù)屬性,然后創(chuàng)建、啟動事務(wù),執(zhí)?業(yè)務(wù)邏輯,最后提交或者回滾事務(wù)。

全部評論
狠狠學(xué)到了
點贊 回復(fù) 分享
發(fā)布于 2023-03-14 12:24 甘肅
又是漲芝士的一天
點贊 回復(fù) 分享
發(fā)布于 2023-03-14 12:15 山東

相關(guān)推薦

點贊 評論 收藏
分享
真漲知識了,原來如此,程序員是要記憶力好才能做的啊~
Bambooinvally:確實如此,https,https,http的復(fù)數(shù)形式
點贊 評論 收藏
分享
評論
點贊
收藏
分享

創(chuàng)作者周榜

更多
??途W(wǎng)
??推髽I(yè)服務(wù)