中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當(dāng)前位置: 首頁 > news >正文

中英文網(wǎng)站asp怎么做seo推廣軟件費(fèi)用

中英文網(wǎng)站asp怎么做,seo推廣軟件費(fèi)用,中國現(xiàn)任領(lǐng)導(dǎo)名單簡介,2022最新國內(nèi)新聞50條簡短當(dāng)構(gòu)建復(fù)雜的企業(yè)級應(yīng)用程序時,數(shù)據(jù)一致性和可靠性是至關(guān)重要的。Spring 框架提供了強(qiáng)大而靈活的事務(wù)管理機(jī)制,成為開發(fā)者處理事務(wù)的首選工具。本文將深入探討 Spring 事務(wù)的使用和原理,為大家提供全面的了解和實(shí)際應(yīng)用的指導(dǎo)。 本文概覽 首…

當(dāng)構(gòu)建復(fù)雜的企業(yè)級應(yīng)用程序時,數(shù)據(jù)一致性和可靠性是至關(guān)重要的。Spring 框架提供了強(qiáng)大而靈活的事務(wù)管理機(jī)制,成為開發(fā)者處理事務(wù)的首選工具。本文將深入探討 Spring 事務(wù)的使用和原理,為大家提供全面的了解和實(shí)際應(yīng)用的指導(dǎo)。

本文概覽

  • 首先,我們將從事務(wù)的基礎(chǔ)出發(fā),介紹其概念、生命周期、隔離級別、傳播行為。
  • 其次,我們再介紹在 Spring 中,如何應(yīng)用聲明式和編程式兩種事務(wù)管理方式。
  • 最后,我們將深入研究 Spring 事務(wù)的原理,了解其核心組件和關(guān)鍵類,解析其工作原理,探索它是如何做到將事務(wù)的控制與業(yè)務(wù)邏輯進(jìn)行解耦的。

事務(wù)基礎(chǔ)

事務(wù)簡介

在數(shù)據(jù)庫和軟件開發(fā)領(lǐng)域,事務(wù)是一組相關(guān)的操作,被視為不可分割的執(zhí)行單位。事務(wù)具有四個關(guān)鍵數(shù)據(jù),簡稱 ACID 屬性:

  • 原子性(Atomicity):事務(wù)是原子的,它要么全部執(zhí)行成功,要么完全不執(zhí)行。如果事務(wù)的任何部分失敗,整個事務(wù)將回滾到初始狀態(tài),不會留下部分完成的結(jié)果。
  • 一致性(Consistency):事務(wù)在執(zhí)行前后,數(shù)據(jù)庫的狀態(tài)應(yīng)保持一致。這意味著事務(wù)的執(zhí)行不會破壞數(shù)據(jù)庫的完整性約束,如唯一性約束、外鍵約束等。
  • 隔離性(Isolation):多個事務(wù)并發(fā)執(zhí)行時,每個事務(wù)都應(yīng)該被隔離,以防止彼此之間的干擾。數(shù)據(jù)庫系統(tǒng)通過事務(wù)隔離級別來定義事務(wù)之間的隔離程度。
  • 持久性(Durability):一旦事務(wù)成功完成,其結(jié)果應(yīng)該是持久的,即使在系統(tǒng)故障或重啟后也應(yīng)該保持。數(shù)據(jù)庫系統(tǒng)通常通過將事務(wù)的結(jié)果寫入日志文件來實(shí)現(xiàn)持久性。

事務(wù)的生命周期通常包括一下階段:

  • 開始:事務(wù)開始時,系統(tǒng)記錄數(shù)據(jù)庫的初始狀態(tài)。
  • 執(zhí)行:事務(wù)執(zhí)行相關(guān)的數(shù)據(jù)庫操作,可能包括插入、更新、刪除等。
  • 提交:如果事務(wù)成功執(zhí)行,將對數(shù)據(jù)庫的更改提交,使其成為永久性的。
  • 回滾:如果在事務(wù)執(zhí)行期間發(fā)生錯誤或者事務(wù)被顯示混滾,系統(tǒng)將撤銷事務(wù)中的所有更改,回復(fù)數(shù)據(jù)庫到事務(wù)開始時的狀態(tài)。

下面我們通過一些例子來深入理解下事務(wù)的生命周期過程:

案例一:開啟事務(wù)并插入一條數(shù)據(jù),執(zhí)行成功并提交事務(wù)

-- 開始事務(wù)
BEGIN;
-- 執(zhí)行數(shù)據(jù)庫操作,向 `user` 表中插入一條數(shù)據(jù)
INSERT INTO `user` (name,age,address) VALUE ("帥氣的小張",25,"山東菏澤");
-- 提交事務(wù)
COMMIT;

案例二:開啟事務(wù)插入兩條數(shù)據(jù),其中第二條數(shù)據(jù)執(zhí)行異常,事務(wù)發(fā)生回滾,那么第一條數(shù)據(jù)并沒有生效

-- 開始事務(wù)
BEGIN;
-- 執(zhí)行操作,向 `user` 表中插入一條數(shù)據(jù)
INSERT INTO `user` (name,age,address) VALUE ("帥氣的小張",25,"山東菏澤");
-- 執(zhí)行一條異常操作 address 字段拼錯
INSERT INTO `user` (name,age,adress) VALUE ("帥氣的小張",25,"山東菏澤");
-- 回滾事務(wù)
ROLLBACK;

可以看到,在 MySQL 里,執(zhí)行事務(wù)的操作包括 BEGIN(開啟)、COMMIT(提交)、ROLLBACK(回滾)

案例三:使用 Spring 框架時,進(jìn)行聲明式事務(wù)管理

package com.markus.spring.transaction.service;import com.markus.spring.data.jdbc.domain.entity.User;
import com.markus.spring.data.jdbc.repository.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;import java.util.List;
import java.util.Objects;/*** @Author: zhangchenglong06* @Date: 2024/2/2* @Description:*/
@Service
public class UserService {@Autowiredprivate UserDao userDao;@Transactionalpublic void processUser() {User user = new User();user.setName("markus zhang unique time :" + System.currentTimeMillis());user.setAge(25);user.setAddress("山東菏澤");// 1. 先向數(shù)據(jù)庫中插入一條數(shù)據(jù)userDao.insertUser(user);// 故意拋出一個異常,驗(yàn)證下 第一步 的操作是否會回滾int i = 1 / 0;// 2. 再查詢該數(shù)據(jù)User queryUserByName = userDao.queryUserByName(user.getName());if (Objects.isNull(queryUserByName)) {return;}// 3. 再更新該數(shù)據(jù)到數(shù)據(jù)庫中queryUserByName.setAddress("北京朝陽");userDao.updateUser(queryUserByName);}
}

案例四:使用 Spring 框架時,進(jìn)行編程式事務(wù)管理

package com.markus.spring.transaction.service;import com.markus.spring.data.jdbc.domain.entity.User;
import com.markus.spring.data.jdbc.repository.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;import java.util.List;
import java.util.Objects;/*** @Author: zhangchenglong06* @Date: 2024/2/2* @Description:*/
@Service
public class UserService {@Autowiredprivate UserDao userDao;@Autowiredprivate TransactionTemplate transactionTemplate;public void processUserByProgram() {User user = new User();user.setName("markus zhang unique time :" + System.currentTimeMillis());user.setAge(25);user.setAddress("山東菏澤");transactionTemplate.execute(new TransactionCallbackWithoutResult() {@Overrideprotected void doInTransactionWithoutResult(TransactionStatus status) {try {processUser();} catch (Exception e) {// 如果發(fā)生異常,回滾事務(wù)status.setRollbackOnly();throw e;}}});}public void processUser() {User user = new User();user.setName("markus zhang unique time :" + System.currentTimeMillis());user.setAge(25);user.setAddress("山東菏澤");// 1. 先向數(shù)據(jù)庫中插入一條數(shù)據(jù)userDao.insertUser(user);// 故意拋出一個異常,驗(yàn)證下 第一步 的操作是否會回滾int i = 1 / 0;// 2. 再查詢該數(shù)據(jù)User queryUserByName = userDao.queryUserByName(user.getName());if (Objects.isNull(queryUserByName)) {return;}// 3. 再更新該數(shù)據(jù)到數(shù)據(jù)庫中queryUserByName.setAddress("北京朝陽");userDao.updateUser(queryUserByName);}public List<User> queryAllUsers() {List<User> users = userDao.queryUsers(-1);return users;}
}

事務(wù)的隔離級別

上面在講隔離性的時候提到數(shù)據(jù)庫通過事務(wù)隔離級別來實(shí)現(xiàn)隔離性,那什么是事務(wù)隔離級別呢?

它定義了多個事務(wù)之間相互影響的程度,以及它們能否同時運(yùn)行。在數(shù)據(jù)庫中,有四個標(biāo)準(zhǔn)的隔離級別,分別是讀未提交(Read Uncommitted)、讀已提交(Read Committed)、可重復(fù)讀(Repeatable Read)和串行化(Serializable)。

讀未提交(Read Uncommitted)
  • 允許事務(wù)讀取其他事務(wù)未提交的數(shù)據(jù)
  • 它是最低的事務(wù)隔離級別,存在臟讀問題,即一個事務(wù)讀取到了另一個事務(wù)未提交的數(shù)據(jù)。

我們通過一個示例來理解一下:

# session one
-- 開始事務(wù)
BEGIN;
-- 執(zhí)行數(shù)據(jù)庫操作,向 `user` 表中插入一條數(shù)據(jù)
INSERT INTO `user` (name,age,address) VALUE ("帥氣的小張",25,"山東菏澤");
-- 提交事務(wù)
COMMIT;# session two
SELECT * FROM `user`

按照下圖執(zhí)行順序可以看到,會話二的查詢操作執(zhí)行時,可以讀到會話一還未提交的數(shù)據(jù)。

image-20240203172425647

讀已提交(Read Committed)
  • 保證一個事務(wù)提交后才被其他事務(wù)讀取。
  • 解決了臟讀的問題,但仍存在不可重復(fù)讀問題,即一個事務(wù)(A)在兩次讀取之間,另一個事務(wù)(B)修改了數(shù)據(jù),導(dǎo)致 A 兩次讀取的數(shù)據(jù)不一致。

我們先來看下 讀已提交解決臟讀的場景

image-20240203172856676

可以看出,再次執(zhí)行事務(wù)一的插入操作后(未提交事務(wù)),事務(wù)二執(zhí)行查詢邏輯時,并沒有查詢到數(shù)據(jù)。

我們再來看下產(chǎn)生不可重復(fù)讀的問題,也就是在同一事務(wù)中前后讀取的數(shù)據(jù)不一致。

image-20240203173835615

可重復(fù)讀(Repeatable Read)
  • 保證一個事務(wù)在其生命周期內(nèi)多次讀取同一數(shù)據(jù)時,得到的結(jié)果是一致的。
  • 解決了不可重復(fù)讀的問題,但仍然可能存在幻讀問題,即一個事務(wù)在兩次查詢之間,另一個事務(wù)插入了新的數(shù)據(jù)。

如下圖所示,可以看出,可重復(fù)讀事務(wù)隔離級別可以解決同一事務(wù)中讀取到不同的數(shù)據(jù)問題,但實(shí)際上,這可能也是一種虛假的數(shù)據(jù),也就是幻讀。

image-20240203175706512

串行化(Serializable)
  • 這是最高的事務(wù)隔離級別,確保事務(wù)之間不會發(fā)生臟讀、不可重復(fù)讀和幻讀。
  • 通過對事務(wù)進(jìn)行串行化來避免并發(fā)問題,但可能導(dǎo)致性能下降,因?yàn)樗枞似渌聞?wù)的并發(fā)執(zhí)行。

image-20240203181908920

Spring 中的應(yīng)用

在 Spring 中,事務(wù)隔離級別通過 Isolation 接口表示。

public enum Isolation {DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);private final int value;Isolation(int value) {this.value = value;}public int value() {return this.value;}}

在 Spring 中,事務(wù)隔離級別可以通過@Transactional注解或者TransactionDefinition接口進(jìn)行設(shè)置。例如:

通過注解:

@Transactional(isolation = Isolation.READ_COMMITTED)
public void someTransactionalMethod() {// 事務(wù)處理邏輯
}

通過編程:

 public void processUserByProgram() {User user = new User();user.setName("markus zhang unique time :" + System.currentTimeMillis());user.setAge(25);user.setAddress("山東菏澤");// 設(shè)置 事務(wù)隔離級別transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);// 執(zhí)行 事務(wù)transactionTemplate.execute(new TransactionCallbackWithoutResult() {@Overrideprotected void doInTransactionWithoutResult(TransactionStatus status) {try {processUser();} catch (Exception e) {// 如果發(fā)生異常,回滾事務(wù)status.setRollbackOnly();throw e;}}});}

事務(wù)的傳播行為

事務(wù)的傳播行為定義了當(dāng)一個事務(wù)方法被另一個事務(wù)方法調(diào)用時,他們之間的交互方式,以及新事務(wù)如何與已有事務(wù)進(jìn)行關(guān)聯(lián)。Spring 框架引入了事務(wù)傳播行為的概念,并提供了靈活的事務(wù)管理機(jī)制,使得開發(fā)者可以根據(jù)具體需求配置事務(wù)的傳播行為??梢酝ㄟ^@Transactional注解或者編程式事務(wù)管理,Spring 允許開發(fā)這選擇合適的傳播行為,以適應(yīng)各種業(yè)務(wù)場景。下面我們來學(xué)習(xí)下幾種常見的事務(wù)傳播行為。

REQUIRED(默認(rèn)值)
  • 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則創(chuàng)建一個新的事務(wù)。
  • 這是最常見的傳播行為,適用于大多數(shù)情況。
@Transactional(propagation = Propagation.REQUIRED)
public void transactionalMethod() {// 事務(wù)處理邏輯
}
REQUIRED_NEW
  • 總是創(chuàng)建一個新的事務(wù),如果當(dāng)前存在事務(wù),則將其掛起。
  • 適用于需要獨(dú)立事務(wù)運(yùn)行的情況。
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void transactionalMethod() {// 事務(wù)處理邏輯
}
SUPPORTS
  • 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則以非事務(wù)的方式執(zhí)行。
  • 適用于不需要事務(wù)支持的場景。
@Transactional(propagation = Propagation.SUPPORTS)
public void transactionalMethod() {// 事務(wù)處理邏輯
}
NOT_SUPPORTED
  • 以非事務(wù)的方式執(zhí)行,如果當(dāng)前存在事務(wù),則將其掛起。
  • 適用于不希望在事務(wù)中執(zhí)行的場景。
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void transactionalMethod() {// 事務(wù)處理邏輯
}
NEVER
  • 以非事務(wù)的方式執(zhí)行,如果當(dāng)前存在事務(wù),則拋出異常。
  • 適用于不希望在事務(wù)中執(zhí)行,則確保不會存在事務(wù)的場景。
@Transactional(propagation = Propagation.NEVER)
public void transactionalMethod() {// 事務(wù)處理邏輯
}
MANDATORY
  • 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒有事務(wù),則拋出異常。
  • 適用于要求在已存在的事務(wù)中運(yùn)行的場景。
@Transactional(propagation = Propagation.MANDATORY)
public void transactionalMethod() {// 事務(wù)處理邏輯
}
NESTED
  • 如果當(dāng)前存在事務(wù),則創(chuàng)建一個嵌套事務(wù),它是當(dāng)前事務(wù)的子事務(wù);如果當(dāng)前沒有事務(wù),則行為類似于 REQUIRED。
  • 適用于需要嵌套事務(wù)支持的場景。
@Transactional(propagation = Propagation.NESTED)
public void transactionalMethod() {// 事務(wù)處理邏輯
}

Spring 事務(wù)使用

在 Spring 中,事務(wù)的實(shí)現(xiàn)有兩種:一種是聲明式事務(wù)管理,也就是通過@Transactional注解聲明;另一種是編程式事務(wù)管理,也就是通過程序?qū)崿F(xiàn)。但不管是聲明式事務(wù)管理還是編程式事務(wù)管理,都需要做的事情就是:

  • 配置 DataSource 數(shù)據(jù)源
  • 配置 TransactionManagement 事務(wù)管理器

代碼就不在這里羅列了,可以參考一下我的github項(xiàng)目 中TransactionModuleApplicationConfig類。

聲明式事務(wù)管理

@Transactional 詳解

@Transactional 是 Spring 框架中用于聲明事務(wù)屬性的注解。我們可以通過在方法或類上添加@Transactional注解,來定義事務(wù)的行為,如隔離級別、傳播行為、超時的。

我們先來來看下該注解的接口定義:

package org.springframework.transaction.annotation;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;import org.springframework.core.annotation.AliasFor;
import org.springframework.transaction.TransactionDefinition;@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {@AliasFor("transactionManager")String value() default "";@AliasFor("value")String transactionManager() default "";String[] label() default {};Propagation propagation() default Propagation.REQUIRED;Isolation isolation() default Isolation.DEFAULT;int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;String timeoutString() default "";boolean readOnly() default false;Class<? extends Throwable>[] rollbackFor() default {};String[] rollbackForClassName() default {};Class<? extends Throwable>[] noRollbackFor() default {};String[] noRollbackForClassName() default {};}
  • value 與 transaction : 用于指定事務(wù)管理器的名稱,表示使用哪個事務(wù)管理器。這對于配置多個事務(wù)管理器的場景很有用。
  • label : 用于定義事務(wù)的標(biāo)簽,這是在 Spring 5.3 版本引入的新特性。具體如何使用標(biāo)簽取決于實(shí)現(xiàn)的事務(wù)管理器實(shí)現(xiàn)。
  • propagation : 用于指定事務(wù)方法的傳播行為,決定事務(wù)方法如何與已存在的事務(wù)進(jìn)行交互。
    • 可選的事務(wù)傳播行為包括 REQUIRED,REQUIRED_NEW,SUPPORTS,NOT_SUPPORTS,NEVER,MANDATORY,NESTED。
    • 默認(rèn)為 REQUIRED。
  • isolation : 用于指定事務(wù)的隔離級別,控制多個事務(wù)之間的相互影響。
    • 可選的事務(wù)隔離級別包括 READ_UNCOMMITTED,READ_COMMITTED,REPEATABLE_READ,SERIALIZABLE。
    • 默認(rèn)為 DEFAULT,取決于 JDBC 的事務(wù)隔離級別,依賴于數(shù)據(jù)庫。
  • timeout : 指定事務(wù)的超時時間,單位為秒。如果事務(wù)執(zhí)行時間超過設(shè)定的超時時間,將會被回滾。默認(rèn)為-1,表示沒有超時時間。
  • timeoutString : 允許將超時時間表示為字符串,例如使用占位符。作用同 timeout ,控制事務(wù)的執(zhí)行時間。
  • readOnly : 用于指定事務(wù)是否為只讀事務(wù)。如果設(shè)置為true,表示只進(jìn)行讀取數(shù)據(jù)庫操作,可以優(yōu)化事務(wù)(無需鎖定資源、減少回滾風(fēng)險、提高并發(fā)性能、降低資源消耗、數(shù)據(jù)庫優(yōu)化器的選擇)。
  • rollbackFor 與 rollbackForClassName : 用于指定在哪些異常情況下會回滾事務(wù),可以執(zhí)行異常類型的類型數(shù)組。
  • noRollbackFor 與 noRollbackForClassName : 用于指定哪些異常下不會回滾事務(wù),可以指定異常的類型數(shù)組。
使用示例

我們來看下聲明式事務(wù)管理的示例:

package com.markus.spring.transaction.service;import com.markus.spring.data.jdbc.domain.entity.User;
import com.markus.spring.data.jdbc.repository.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;import java.util.List;/*** @author: markus* @date: 2024/2/3 10:19 PM* @Description:* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
@Service
public class SpringTransactionService {@Autowiredprivate UserDao userDao;// 事務(wù)傳播行為 默認(rèn) REQUIRED@Transactional(rollbackFor = IllegalArgumentException.class, noRollbackFor = IllegalStateException.class)public void method() {User user = new User();long currentTime = System.currentTimeMillis();System.out.println("currentTime : " + currentTime);user.setName("帥氣的小張 " + currentTime);user.setAge(25);user.setAddress("山東菏澤");// 向數(shù)據(jù)庫中插入一條數(shù)據(jù)userDao.insertUser(user);// 拋出該異常會回滾
//        throw new IllegalArgumentException("違規(guī)參數(shù)");// 拋出該異常不會回滾throw new IllegalStateException("違規(guī)狀態(tài)");}@Transactional(readOnly = true)public List<User> queryUsers() {return userDao.queryUsers(0);}}
package com.markus.spring.transaction.service;import com.markus.spring.data.jdbc.domain.entity.User;
import com.markus.spring.transaction.config.TransactionModuleApplicationConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import java.util.List;/*** @author: markus* @date: 2024/2/3 10:22 PM* @Description:* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TransactionModuleApplicationConfig.class)
public class TransactionServiceTest {@Autowiredprivate SpringTransactionService springTransactionService;@Testpublic void testMethod() {springTransactionService.method();}@Testpublic void testQueryUsers() {List<User> users = springTransactionService.queryUsers();users.forEach(System.out::println);}
}

編程式事務(wù)管理

編程式事務(wù)管理是通過編寫代碼顯示管理事務(wù)的一種方式,相對于聲明式事務(wù)管理,它更加靈活,但也需要我們更深入地理解事務(wù)管理的細(xì)節(jié)。對于一些底層 API 我們不在此處贅述,重點(diǎn)講述如何通過代碼來顯示管理事務(wù)。

如何使用

我們直接上代碼:

package com.markus.spring.transaction.service;import com.markus.spring.data.jdbc.domain.entity.User;
import com.markus.spring.data.jdbc.repository.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;import static com.markus.spring.data.jdbc.domain.entity.User.createUser;/*** @author: markus* @date: 2024/2/4 12:17 AM* @Description:* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
@Service
public class ProgrammaticTransactionService {@Autowiredprivate UserDao userDao;@Autowiredprivate TransactionTemplate transactionTemplate;public void method() {User user = createUser();transactionTemplate.execute(new TransactionCallbackWithoutResult() {@Overrideprotected void doInTransactionWithoutResult(TransactionStatus status) {try {userDao.insertUser(user);} catch (Exception e) {// 捕獲異常,并將事務(wù)回滾status.setRollbackOnly();// 并將異常跑出去throw e;}}});}
}

在上面聲明式事務(wù)管理中,我們可以指定一些屬性;同樣地,編程式事務(wù)管理也可以通過TransactionTemplate設(shè)置。

image-20240204002314546

Spring 事務(wù)實(shí)現(xiàn)

大家在閱讀完上面的內(nèi)容后,應(yīng)該對事務(wù)有了一定的了解和如何使用。想必大家對其底層的實(shí)現(xiàn)機(jī)制有一定的興趣,接下來,我們將深入探討 Spring 事務(wù)管理的核心組件和關(guān)鍵類,解析其工作原理,通過深入源碼的閱讀和分析,我們將更好地的理解 Spring 事務(wù)管理的內(nèi)部機(jī)制,為更高效、更安全地應(yīng)用事務(wù)管理功能提供基礎(chǔ)。

在學(xué)習(xí)源碼前有必要提及的是:你需要掌握 Spring AOP 以及 Bean 生命周期的相關(guān)知識。因?yàn)?Spring 事務(wù)是在 Bean 生命周期環(huán)節(jié)對符合條件的 Bean 進(jìn)行代理,通過 AOP 對類或者方法進(jìn)行增強(qiáng)。

核心組件和關(guān)鍵類

TransactionManagement

它是在 Spring 5.2 版本被提出來的一個用來統(tǒng)一表示傳統(tǒng)事務(wù)管理和響應(yīng)式事務(wù)管理的標(biāo)記接口。其實(shí)現(xiàn)類如下所示:

image-20240206144037433

我們重點(diǎn)來看 PlatformTransactionManagement 及其相關(guān)派生類。PlatformTransactionManagement 定義了 事務(wù)管理 的基本操作。

public interface PlatformTransactionManager extends TransactionManager {/*** 獲取事務(wù)*/TransactionStatus getTransaction(@Nullable TransactionDefinition definition)throws TransactionException;/*** 事務(wù)提交*/void commit(TransactionStatus status) throws TransactionException;/*** 事務(wù)回滾*/void rollback(TransactionStatus status) throws TransactionException;}

對于幾個比較關(guān)鍵類派生類介紹:

  • DataSourceTransactionManagement:它是基于 JDBC 的事務(wù)管理器,適用于單一的 JDBC 數(shù)據(jù)源
    • JdbcTransactionManagement:它繼承與DataSourceTransactionManagement,與之不同的是,它更加靈活,可以與多個不同的 JDBC 數(shù)據(jù)源進(jìn)行交互。
  • JtaTransactionManagement:它是基于 JTA(Java Transaction API)的事務(wù)管理器,支持分布式事務(wù)。比如多個不同的事務(wù)資源,如數(shù)據(jù)庫、消息隊(duì)列等。
InfrastructureAdvisorAutoProxyCreator

InfrastructureAdvisorAutoProxyCreator 用于自動創(chuàng)建 AOP 的代理來應(yīng)用通用的基礎(chǔ)設(shè)施增強(qiáng)器(Advisors),通常用于聲明式事務(wù)管理、安全性檢查、性能監(jiān)控方面。本篇文章介紹的正是他對于聲明式事務(wù)管理方面的應(yīng)用。

image-20240206145517564

通過上圖可以看到,InfrastructureAdvisorAutoProxyCreator 是 AOP 核心組件 AbstraAutoProxyCreator 的實(shí)現(xiàn)之一,目的就是創(chuàng)建 AOP 代理。

TransactionInterceptor

作用如其名,事務(wù)攔截器。是的,它實(shí)現(xiàn)了 AOP 的概念,用于在方法調(diào)用前后織入事務(wù)管理的邏輯。

image-20240206145826965

AnnotationTransactionAttributeSource

AnnotationTransactionAttributeSource 是 Spring 框架用于解析 @Transaction 注解的類之一,它實(shí)現(xiàn)了 TransactinAttributeSource 接口,用于從注解中解析事務(wù)屬性。

image-20240206151034838

BeanFactoryTransactionAttributeSourceAdvisor

BeanFactoryTransactionAttributeSourceAdvisor 是 Spring 框架中用于基于 BeanFactory 的事務(wù)屬性源的增強(qiáng)器(Advisor)。它的作用是根據(jù)配置的事務(wù)屬性源(TransactionAttributeSource)和事務(wù)攔截器(MethodInterceptor),為目標(biāo)方法生成事務(wù)增強(qiáng)器,實(shí)現(xiàn)方法的事務(wù)管理。

image-20240206174840185

工作原理

簡單用一句話描述原理就是:Spring 框架通過 AOP 實(shí)現(xiàn)了對業(yè)務(wù)方法執(zhí)行的事務(wù)管理。詳細(xì)來講就比較復(fù)雜了,需要大家掌握 Spring Bean 生命周期、AOP 攔截、事務(wù)生命周期等。

由于比較復(fù)雜,所以對于 Bean 何時以及如何被代理,我就簡單說下即可,來重點(diǎn)講述下 Spring 框架如何進(jìn)行的事務(wù)管理。

對于本小節(jié)的敘述,如以下所示:

  • 簡單說下目標(biāo) Bean 何時以及如何被代理
  • 詳細(xì)說下事務(wù)構(gòu)建和事務(wù)執(zhí)行
  • TransactionInterceptor#invoke() 流程介紹
自動代理

Spring 通過 InfrastructureAdvisorAutoProxyCreator 實(shí)現(xiàn)對目標(biāo) Bean 對象的代理。具體入口在 org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization,下面來簡單介紹下該方法的執(zhí)行邏輯。

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {if (bean != null) {// 獲取 cacheKey,設(shè)計了緩存來加速 Advisor 的構(gòu)建。// 可以參考 // 		private final Map<Object, Class<?>> proxyTypes = new ConcurrentHashMap<>(16);// 		private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<>(256);Object cacheKey = getCacheKey(bean.getClass(), beanName);if (this.earlyProxyReferences.remove(cacheKey) != bean) {// 構(gòu)造 Advisor 集合并創(chuàng)建出代理return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {// 如果 beanName 不為空并且設(shè)置了自定義的目標(biāo)對象,則不需要被代理。if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {return bean;}// 緩存優(yōu)化,歷史處理過該 Bean 并且知道不需要被代理,直接返回。if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {return bean;}// 如果是 Spring Framework 基礎(chǔ)設(shè)施 Bean 或者 判斷該 Bean 需要跳過,則不需要被代理if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}// 獲取 Advice 數(shù)據(jù)集合并創(chuàng)建最終的代理// getAdvicesAndAdvisorsForBean 方法是一個查找 IoC 容器中 Advice Bean 的方法。// 而我們前面說的 BeanFactoryTransactionAttributeSourceAdvisor 就是在這里獲取到的。Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);if (specificInterceptors != DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);// 創(chuàng)建代理。Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());// 返回代理。return proxy;}this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;
}

上面我們看到了 Spring 是如何給 Bean 進(jìn)行代理的,可能大家會有疑問:怎么判斷當(dāng)前 Bean 是否可以被代理呢?

這就是 AOP 框架中 Pointcut 組件來決策的,針對于事務(wù)功能上,對應(yīng)了 BeanFactoryTransactionAttributeSourceAdvisor,我們前面已經(jīng)介紹過,它是為目標(biāo)方法生成事務(wù)增強(qiáng)器,其中它就包含 Pointcut 實(shí)現(xiàn)即 TransactionAttributeSourcePointcut。我們來看下它的內(nèi)部組成,來探究下:它是如何判斷當(dāng)前 Bean 是否應(yīng)該被代理。

abstract class TransactionAttributeSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {protected TransactionAttributeSourcePointcut() {setClassFilter(new TransactionAttributeSourceClassFilter());}// 判斷 是否被代理 的核心方法@Overridepublic boolean matches(Method method, Class<?> targetClass) {// tas.getTransactionAttribute 獲取 @Transactional 注解元信息,如果該方法攜帶相關(guān)數(shù)據(jù),說明需要被事務(wù)管理。TransactionAttributeSource tas = getTransactionAttributeSource();return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);}/*** Obtain the underlying TransactionAttributeSource (may be {@code null}).* To be implemented by subclasses.*/@Nullableprotected abstract TransactionAttributeSource getTransactionAttributeSource();/*** 用于過濾不需要事務(wù)管理的類*/private class TransactionAttributeSourceClassFilter implements ClassFilter {@Overridepublic boolean matches(Class<?> clazz) {if (TransactionalProxy.class.isAssignableFrom(clazz) ||TransactionManager.class.isAssignableFrom(clazz) ||PersistenceExceptionTranslator.class.isAssignableFrom(clazz)) {return false;}// 判斷 當(dāng)前類是否是攜帶 @Transactional 注解的候選類(在類型、方法上)TransactionAttributeSource tas = getTransactionAttributeSource();return (tas == null || tas.isCandidateClass(clazz));}}}

通過 TransactionAttributeSourcePointcut 的處理,我們可以得出上面的結(jié)論,即 Spring 框架通過 TransactinAttributeSourcePointcut 來篩選目標(biāo)方法,來對方法進(jìn)行事務(wù)管理邏輯增強(qiáng)。

通過上面的討論,在自動代理環(huán)節(jié),我們能得出這樣一個結(jié)論:Spring 框架通過 AOP 實(shí)現(xiàn)了目標(biāo) Bean 的方法增強(qiáng),增加事務(wù)管理的邏輯。我們把其核心類串一下:

  1. Spring 向 IoC 容器注冊 InfrastructureAdvisorAutoProxyCreator Bean,
  2. 該基礎(chǔ)設(shè)施 Bean 可以在業(yè)務(wù)Bean生命周期的初始化后(postProcessAfterInitialization)環(huán)節(jié),對目標(biāo)業(yè)務(wù) Bean 進(jìn)行攔截,獲取容器注冊過的事務(wù)增強(qiáng)器(BeanFactoryTransactionAttributeSourceAdvisor)并為目標(biāo) Bean 生成代理,完成事務(wù)管理邏輯增強(qiáng)。
  3. 而 BeanFactoryTransactionAttributeSourceAdvisor 中的 TransactionAttributeSourcePointcut 完成目標(biāo) Bean 及其方法的篩選攔截。
執(zhí)行事務(wù)

目前,我們已經(jīng)清楚業(yè)務(wù) Bean 如何被事務(wù)管理增強(qiáng),接下來我們繼續(xù)討論當(dāng)應(yīng)用程序調(diào)用被事務(wù)管理的方法時,事務(wù)是如何被執(zhí)行的。

在核心組件介紹 BeanFactoryTransactionAttributeSourceAdvisor 時,它是一個 PoicutcutAdvisor,也就是說它是由 Pointcut 和 Advice 組成。在前面說目標(biāo)方法篩選時Pointcut起到了作用。那么 Advice 則是在執(zhí)行事務(wù)時起到作用,其實(shí)現(xiàn)則是 TransactionInterceptor。為什么說是它的,我們可以在這段代碼中找到答案。

private static class AopAutoProxyConfigurer {public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {Object eleSource = parserContext.extractSource(element);// Create the TransactionAttributeSource definition.RootBeanDefinition sourceDef = new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");sourceDef.setSource(eleSource);sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);// Create the TransactionInterceptor definition.RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);interceptorDef.setSource(eleSource);interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registerTransactionManager(element, interceptorDef);interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);// Create the TransactionAttributeSourceAdvisor definition.RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);advisorDef.setSource(eleSource);advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);if (element.hasAttribute("order")) {advisorDef.getPropertyValues().add("order", element.getAttribute("order"));}parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName));parserContext.registerComponent(compositeDef);}}}

至此,我們已經(jīng)清晰代理方法的入口了,即 org.springframework.transaction.interceptor.TransactionInterceptor#invoke。接下來,我們就重點(diǎn)分析該方法。

@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {// Work out the target class: may be {@code null}.// The TransactionAttributeSource should be passed the target class// as well as the method, which may be from an interface.Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);// Adapt to TransactionAspectSupport's invokeWithinTransaction...return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {@Override@Nullablepublic Object proceedWithInvocation() throws Throwable {return invocation.proceed();}@Overridepublic Object getTarget() {return invocation.getThis();}@Overridepublic Object[] getArguments() {return invocation.getArguments();}});
}@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 TransactionManager tm = determineTransactionManager(txAttr);if (this.reactiveAdapterRegistry != null && tm instanceof ReactiveTransactionManager) {// 針對 響應(yīng)式編程 的處理,我們不關(guān)注,先刪除,大家有興趣可以去看下}// 獲取事務(wù)管理器PlatformTransactionManager ptm = asPlatformTransactionManager(tm);// 獲取目標(biāo)業(yè)務(wù)方法final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {// 這里就是聲明式事務(wù)管理的處理邏輯 @Transactional 注解// Standard transaction demarcation with getTransaction and commit/rollback calls.// 獲取 事務(wù),這里面包括了事務(wù)的傳播行為、事務(wù)隔離級別以及事務(wù)的提交、回滾等信息TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);Object retVal;try {// This is an around advice: Invoke the next interceptor in the chain.// This will normally result in a target object being invoked.// 通常的 攔截器不只有一個,是一個鏈?zhǔn)降?。如上面作者所?#xff0c;這是一個 Around Advice,將會調(diào)用攔截器鏈中下一個攔截器。// 但 返回的結(jié)果 retVal 是 目標(biāo)方法的執(zhí)行結(jié)果。retVal = invocation.proceedWithInvocation();}catch (Throwable ex) {// target invocation exception// 這里的處理則是 對目標(biāo)異常進(jìn)行回滾。目標(biāo)異常外的異常不進(jìn)行回滾completeTransactionAfterThrowing(txInfo, ex);throw ex;}finally {// 清除事務(wù)信息在當(dāng)前線程下cleanupTransactionInfo(txInfo);}if (retVal != null && vavrPresent && VavrDelegate.isVavrTry(retVal)) {// Set rollback-only in case of Vavr failure matching our rollback rules...TransactionStatus status = txInfo.getTransactionStatus();if (status != null && txAttr != null) {retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);}}// 目標(biāo)方法執(zhí)行成功后返回結(jié)果 進(jìn)行事務(wù)提交commitTransactionAfterReturning(txInfo);// 將 目標(biāo)值 返回。return retVal;}else {// 針對 CallbackPreferringPlatformTransactionManager 事務(wù)管理的處理,多處理 編程式事務(wù)管理,我們暫時不關(guān)注,先刪除。}
}

本文總結(jié)

我們總結(jié)一下,本篇文章從事務(wù)基礎(chǔ)介紹開始,講述了什么是事務(wù)、隔離級別、傳播行為,接著又講述了 Spring 事務(wù)的使用即聲明式事務(wù)管理以及編程式事務(wù)管理,最后又講述的 Spring 的 事務(wù)實(shí)現(xiàn)原理包括核心組件、關(guān)鍵類以及事務(wù)的工作原理。

至此,關(guān)于事務(wù)的知識點(diǎn)就講述完了。如果還有其他沒有覆蓋到的地方,歡迎交流。😄

http://www.risenshineclean.com/news/34966.html

相關(guān)文章:

  • 找人做網(wǎng)站應(yīng)該注意什么福州seo兼職
  • 打開鏈接的網(wǎng)站網(wǎng)絡(luò)營銷計劃的七個步驟
  • 自制網(wǎng)站的動態(tài)圖怎么做創(chuàng)意廣告
  • 廣州中小企業(yè)網(wǎng)站建設(shè)免費(fèi)發(fā)帖推廣的平臺
  • 深圳外文網(wǎng)站制作喬拓云智能建站官網(wǎng)
  • 福州企業(yè)網(wǎng)站推廣網(wǎng)絡(luò)營銷推廣方式
  • 馬鞍山制作網(wǎng)站網(wǎng)絡(luò)營銷方式有哪幾種
  • 學(xué)校網(wǎng)站制作2345網(wǎng)址導(dǎo)航大全
  • 做二手房的網(wǎng)站技巧網(wǎng)站做成app
  • 網(wǎng)站設(shè)計價格大概多少谷歌瀏覽器下載手機(jī)版
  • 做網(wǎng)站優(yōu)化給業(yè)務(wù)員提成百度資源提交
  • wordpress+admin主題武漢seo招聘信息
  • 揚(yáng)中網(wǎng)站建設(shè) 優(yōu)幫云站長工具seo查詢5g5g
  • 珠海網(wǎng)站建設(shè)科速互聯(lián)百度知道網(wǎng)頁版進(jìn)入
  • 徐匯做網(wǎng)站無錫百度推廣公司哪家好
  • 青島做外貿(mào)網(wǎng)站建設(shè)網(wǎng)絡(luò)營銷服務(wù)的特點(diǎn)
  • 微信網(wǎng)站模板免費(fèi)下載seo免費(fèi)入門教程
  • wordpress foxseo 關(guān)鍵詞優(yōu)化
  • 快速免費(fèi)建網(wǎng)站常用的營銷策略
  • 邢臺做網(wǎng)站優(yōu)化百度排名優(yōu)化軟件
  • 淮北做網(wǎng)站的公司百度seo優(yōu)化服務(wù)項(xiàng)目
  • 滎陽網(wǎng)站建設(shè)公司網(wǎng)絡(luò)關(guān)鍵詞優(yōu)化軟件
  • 便宜做網(wǎng)站seo算法優(yōu)化
  • 佛山正規(guī)網(wǎng)站建設(shè)報價優(yōu)化大師app下載安裝
  • 口碑好網(wǎng)站建設(shè)公司seo關(guān)鍵詞優(yōu)化平臺
  • 不同網(wǎng)站對商家做o2o的政策阿里seo排名優(yōu)化軟件
  • 湖南教育平臺網(wǎng)站建設(shè)流量寶
  • 梧州專業(yè)網(wǎng)站推廣官方百度平臺
  • 做外貿(mào)的網(wǎng)站主要有哪些內(nèi)容網(wǎng)站分析培訓(xùn)班
  • 智慧團(tuán)建登錄入口官網(wǎng)排名輕松seo 網(wǎng)站推廣