国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

61 張圖,剖析 Spring 事務,就是要鉆到底!

開發 架構
為了方便大家能更好看懂后面的源碼,我先整體介紹一下源碼的執行流程,讓大家有一個整體的認識,否則容易被繞進去。

大家好,我是樓仔!

下面我會簡單介紹一下 Spring 事務的基礎知識,以及使用方法,然后直接對源碼進行拆解。

不 BB,上文章目錄。

圖片

1. 項目準備

需要搭建環境的同學,代碼詳見:https://github.com/lml200701158/program_demo/tree/main/spring-transaction

下面是 DB 數據和 DB 操作接口:

uid

uname

usex

1

張三

2

陳恒

3

樓仔

// 提供的接口
public interface UserDao {
// select * from user_test where uid = "#{uid}"
public MyUser selectUserById(Integer uid);
// update user_test set uname =#{uname},usex = #{usex} where uid = #{uid}
public int updateUser(MyUser user);
}

基礎測試代碼,testSuccess() 是事務生效的情況:

@Service
public class Louzai {
@Autowired
private UserDao userDao;

public void update(Integer id){
MyUser user = new MyUser();
user.setUid(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("事務生效");
}
}

執行入口:

public class SpringMyBatisTest {
public static void main(String[] args) throws Exception {
String xmlPath = "applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
Louzai uc = (Louzai) applicationContext.getBean("louzai");
uc.testSuccess();
}
}

輸出:

16:44:38.267 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.transaction.interceptor.TransactionInterceptor#0'
16:44:38.363 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'txManager'
16:44:40.966 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Creating new transaction with name [com.mybatis.controller.Louzai.testSuccess]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-java.lang.Exception
16:44:40.968 [main] DEBUG org.springframework.jdbc.datasource.DriverManagerDataSource - Creating new JDBC DriverManager Connection to [jdbc:mysql://127.0.0.1:3306/java_study?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimeznotallow=Asia/Shanghai]
16:44:41.228 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Acquired Connection [com.mysql.cj.jdbc.ConnectionImpl@5b5caf08] for JDBC transaction
16:44:41.231 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Switching JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5b5caf08] to manual commit
原記錄:MyUser(uid=1, uname=張三, usex=)
16:42:59.345 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Initiating transaction rollback
16:42:59.346 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Rolling back JDBC transaction on Connection [com.mysql.cj.jdbc.ConnectionImpl@70807224]
16:42:59.354 [main] DEBUG org.springframework.jdbc.datasource.DataSourceTransactionManager - Releasing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@70807224] after transaction
Exception in thread "main" java.lang.Exception: 事務生效
at com.mybatis.controller.Louzai.testSuccess(Louzai.java:34)
// 異常日志省略...

2. Spring 事務工作流程

為了方便大家能更好看懂后面的源碼,我先整體介紹一下源碼的執行流程,讓大家有一個整體的認識,否則容易被繞進去。

整個 Spring 事務源碼,其實分為 2 塊,我們會結合上面的示例,給大家進行講解。圖片

第一塊是后置處理,我們在創建 Louzai Bean 的后置處理器中,里面會做兩件事情:

獲取 Louzai 的切面方法:首先會拿到所有的切面信息,和 Louzai 的所有方法進行匹配,然后找到 Louzai 所有需要進行事務處理的方法,匹配成功的方法,還需要將事務屬性保存到緩存 attributeCache 中。

創建 AOP 代理對象:結合 Louzai 需要進行 AOP 的方法,選擇 Cglib 或 JDK,創建 AOP 代理對象。

圖片

第二塊是事務執行,整個邏輯比較復雜,我只選取 4 塊最核心的邏輯,分別為從緩存拿到事務屬性、創建并開啟事務、執行業務邏輯、提交或者回滾事務。

3. 源碼解讀

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

上面的知識都不難,下面才是我們的重頭戲,讓你跟著樓仔,走一遍代碼流程。

3.1 代碼入口

圖片

圖片

這里需要多跑幾次,把前面的 beanName 跳過去,只看 louzai。

圖片

圖片

進入 doGetBean(),進入創建 Bean 的邏輯。

圖片

進入 createBean(),調用 doCreateBean()。

圖片

進入 doCreateBean(),調用 initializeBean()。

圖片

圖片

圖片

圖片

如果看過我前面幾期系列源碼的同學,對這個入口應該會非常熟悉,其實就是用來創建代理對象。

3.2 創建代理對象

圖片

這里是重點!敲黑板!!!

先獲取 louzai 類的所有切面列表;

創建一個 AOP 的代理對象。

圖片

3.2.1 獲取切面列表

圖片

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

圖片

圖片

圖片

圖片

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

圖片

圖片

圖片

圖片

進入 canApply(),開始匹配 louzai 的切面。

圖片

這里是重點!敲黑板!!!

這里只會匹配到 Louzai.testSuccess() 方法,我們直接進入匹配邏輯。

圖片

如果匹配成功,還會把事務的屬性配置信息放入 attributeCache 緩存。

圖片

圖片

圖片

圖片

圖片

圖片

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

圖片

再回到該小節開頭,我們拿到 louzai 的切面信息,去創建 AOP 代理對象。

圖片

3.2.2 創建 AOP 代理對象

創建 AOP 代理對象的邏輯,在上一篇文章(Spring AOP)講解過,我是通過 Cglib 創建,感興趣的同學可以關注公眾號「樓仔」,翻一下樓仔的歷史文章。

3.3 事務執行

回到業務邏輯,通過 louzai 的 AOP 代理對象,開始執行主方法。

圖片

因為代理對象是 Cglib 方式創建,所以通過 Cglib 來執行。

圖片

圖片

圖片

圖片

這里是重點!敲黑板!!!

下面的代碼是事務執行的核心邏輯 invokeWithinTransaction()。

圖片

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

//獲取我們的事務屬源對象
TransactionAttributeSource tas = getTransactionAttributeSource();
//通過事務屬性源對象獲取到我們的事務屬性信息
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
//獲取我們配置的事務管理器對象
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
//從tx屬性對象中獲取出標注了@Transactionl的方法描述符
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

//處理聲明式事務
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
//有沒有必要創建事務
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);

Object retVal;
try {
//調用鉤子函數進行回調目標方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
//拋出異常進行回滾處理
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
//清空我們的線程變量中transactionInfo的值
cleanupTransactionInfo(txInfo);
}
//提交事務
commitTransactionAfterReturning(txInfo);
return retVal;
}
//編程式事務
else {
// 這里不是我們的重點,省略...
}
}

3.3.1 獲取事務屬性

在 invokeWithinTransaction() 中,我們找到獲取事務屬性的入口。

圖片

從 attributeCache 獲取事務的緩存數據,緩存數據是在 “2.2.1 獲取切面列表” 中保存的。

圖片

3.3.2 創建事務

圖片

圖片

圖片

通過 doGetTransaction() 獲取事務。

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

通過 startTransaction() 開啟事務。

圖片

下面是開啟事務的詳細邏輯,了解一下即可。

protected void doBegin(Object transaction, TransactionDefinition definition){
//強制轉化事務對象
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;

try {
//判斷事務對象沒有數據庫連接持有器
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
//通過數據源獲取一個數據庫連接對象
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
//把我們的數據庫連接包裝成一個ConnectionHolder對象 然后設置到我們的txObject對象中去
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}

//標記當前的連接是一個同步事務
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();

//為當前的事務設置隔離級別
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);

//關閉自動提交
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}

//判斷事務為只讀事務
prepareTransactionalConnection(con, definition);
//設置事務激活
txObject.getConnectionHolder().setTransactionActive(true);

//設置事務超時時間
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}

// 綁定我們的數據源和連接到我們的同步管理器上 把數據源作為key,數據庫連接作為value 設置到線程變量中
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);
}
}

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

圖片

3.3.3 執行邏輯

還是在 invokeWithinTransaction() 中,開始執行業務邏輯。

圖片

圖片

圖片

圖片

圖片

進入到真正的業務邏輯。

圖片

執行完畢后拋出異常,依次返回,走后續的回滾事務邏輯。

3.3.4 回滾事務

還是在 invokeWithinTransaction() 中,進入回滾事務的邏輯。

圖片

圖片

執行回滾邏輯很簡單,我們只看如何判斷是否回滾。

圖片

圖片

圖片

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

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

圖片

到這里,所有的流程結束。

4. 結語

我們再小節一下,文章先介紹了事務的使用示例,以及事務的執行流程。

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

先匹配出 louzai 對象所有關于事務的切面列表,并將匹配成功的事務屬性保存到緩存;

從緩存取出事務屬性,然后創建、啟動事務,執行業務邏輯,最后提交或者回滾事務。

責任編輯:武曉燕 來源: 樓仔
相關推薦

2023-09-28 21:37:41

HashMap多線程

2023-09-11 22:19:38

Spring啟動技術

2014-08-25 09:12:47

Spring事務管理

2010-03-29 13:34:15

ibmdwSpring

2010-03-23 08:46:40

Spring

2021-04-21 12:29:45

KafkaZookeeper模型

2023-10-12 08:54:20

Spring事務設置

2023-10-10 08:16:07

Spring依賴注入SpEL表達式

2017-04-06 10:11:26

金蝶

2022-07-04 11:06:02

RocketMQ事務消息實現

2020-05-06 09:10:46

AQS同步器CAS

2020-11-30 14:40:52

事務系統項目

2025-12-10 06:05:00

2025-12-10 09:30:34

2009-09-08 16:20:12

LINQ to SQL

2025-12-09 06:20:00

Paimon數據湖數據庫

2022-10-11 08:27:45

Spring事務管理性能統計

2010-03-02 13:43:01

WCF事務演示

2018-10-17 18:46:53

數字銀行創業公司用戶

2010-06-12 14:35:46

UML對象圖
點贊
收藏

51CTO技術棧公眾號

国产乱论精品| 欧美日韩亚洲国产| 亚洲国产成人精品一区二区 | 精品欧美一区免费观看α√| 黄色免费高清视频| 国产乱码精品一区二三赶尸艳谈| 欧美尤物一区| 2017欧美狠狠色| 欧美精品1区| 国产在线精品不卡| 视频一区二区三区在线看免费看| 一区二区三区亚洲| 欧美videossexotv100| 91成人在线精品视频| 精品国产一区二区三区麻豆免费观看完整版 | 亚洲小说区图片区情欲小说| 精品国产伦一区二区三区观看体验 | 欧美一区免费视频| 欧美日韩一级二级三级| 亚洲伊人春色| 最近中文字幕mv2018在线高清| 久久精品一本久久99精品| 综合国产在线| 五月激情在线| 国产精自产拍久久久久久| 亚洲综合区在线| 亚洲二区视频| 一区二区三区短视频| 免费av网址在线| 古典武侠综合av第一页| 欧美精品乱码久久久久久| 国产一区网站| 欧美亚洲精品一区二区| 亚洲青青青在线视频| 日韩精品一区二区久久| 91av亚洲| 天堂中文字幕| 欧美一级中文字幕| 91久久久久久久| 欧美日韩国产va另类| 91精品国产一区二区| 亚洲高清免费视频| 国产一区二区三区av电影| 少妇精品久久久一区二区| 欧美aaaaaaa| 在线中文字幕资源| 成人在线观看亚洲| 久久九九国产视频| 色综合视频二区偷拍在线| 国产精品尤物福利片在线观看| 亚洲性线免费观看视频成熟| 欧美男男青年gay1069videost | 黄色网一区二区| 91精选在线| 又黄又爽毛片免费观看| 一区二区在线看| 国产精品理伦片| 91理论电影在线观看| 久久综合国产精品| 亚洲图片你懂的| 成人av电影在线观看| 国产精品一二| 欧美日韩18| 欧美综合社区国产| 日本高清网站| 性欧美大战久久久久久久| 国产有码在线一区二区视频| 亚洲片在线资源| 欧美性感一区二区三区| 久久久久免费| 成人资源在线| 深夜av在线| 色大18成网站www在线观看| 欧美一区二区三区少妇| 在线观看国产高清视频| 在线碰免费视频在线观看| 美女的尿口免费视频| 久久精品影视大全| 欧美又大又粗又长| 亚洲人成7777| 在线欧美不卡| 精品福利网址导航| 欧美尤物美女在线| av在线天天| 中国一级黄色录像| 国产精品444| 欧美—级a级欧美特级ar全黄| 国产最新精品| 国内精品久久久久久99蜜桃| 午夜网站在线观看| 曰韩不卡视频| 欧美孕妇性xx| 国产亚洲人成a一在线v站| 色偷偷久久一区二区三区| 亚瑟在线精品视频| 亚洲少妇自拍| 九热爱视频精品视频| 日韩欧美一区中文| 国产aⅴ精品一区二区三区久久| 国产中文在线观看| 337p日本| 中文字幕视频在线免费观看| 日本一区二区三区视频免费看| 男女视频在线看| 伊人av综合网| 福利二区91精品bt7086| 99久久精品国产一区二区三区| 亚洲激情中文在线| 要久久爱电视剧全集完整观看| 91精品国产一区二区在线观看 | 欧美军人男男激情gay| av成人免费看| 伊人久久国产| 久久久精品一区二区毛片免费看| 伊人222成人综合网| 人妻熟妇乱又伦精品视频| 国产日韩av在线播放| 91日韩一区二区三区| 夜夜嗨一区二区| 麻豆精品国产91久久久久久| 亚洲综合好骚| 经典一区二区三区| 免费视频最近日韩| 国产一区三区三区| 成人av午夜电影| 2020国产成人综合网| 亚洲人成影院在线观看| 欧美在线短视频| 精品国产乱码久久久久久1区2区 | 在线视频网站| 免费涩涩18网站入口| 91插插插插插插插插| 超级污的网站| 97超碰国产一区二区三区| 亚洲性图自拍| 国产色99精品9i| 性欧美gay| 日韩免费啪啪| 都市激情一区| 国产成人精品一区二三区在线观看 | 一道本一区二区三区| 国产精品wwww| 91九色偷拍| 日韩一区二区三区高清| 精品成在人线av无码免费看| 欧美日韩亚洲综合一区二区三区| 中文字幕av一区二区三区四区| 中文字幕成人| 丁香天五香天堂综合| 亚洲福利精品在线| 免费在线观看污污视频| 在线精品亚洲欧美日韩国产| 欧美福利影院| 免费看成年人视频在线观看| 亚洲黄色a v| 国产丝袜在线| 成人精品动漫一区二区三区| 秋霞av亚洲一区二区三| 6080日韩午夜伦伦午夜伦| 国产欧美日韩高清| 国产成人精品视频ⅴa片软件竹菊| 国产传媒在线| 欧美综合二区| 黑人精品xxx一区| 国产美女91呻吟求| 在线香蕉视频| 国内精品久久久久久久久电影网| 国产精品久久久久久妇女6080| 亚洲欧美日韩国产精品| 欧美少妇一级片| 色资源二区在线视频| 久久男女视频| 136国产福利精品导航| 91精品成人久久| 网上成人av| av中字幕久久| 欧美影视一区二区三区| 九色91在线视频| 亚洲黄色免费看| 国产清纯白嫩初高生在线观看91 | 欧美日韩国产中文| 日日噜噜噜噜夜夜爽亚洲精品| 免费污视频在线观看| 日本一区二区三区在线不卡| 国产精品久久久久福利| av网站在线免费| 黄网视频在线观看| 麻豆理论在线观看| 亚洲桃色在线一区| 国产精品色午夜在线观看| 色三级在线观看| 久久精品一区二区三区四区| 久久国内精品自在自线400部| 最近中文字幕日韩精品| 在线激情av| 91视频在线观看免费| 国产91精品入口17c| 精品视频一区二区三区| 日本久久电影网| 天堂视频福利|