自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

炸裂!@Transactional遇上@Async:是"王炸組合"還是"致命事故"

開發(fā) 前端
@Transactional?是Spring事務(wù)管理的核心注解,用于聲明方法需要在事務(wù)上下文中執(zhí)行,確保數(shù)據(jù)庫(kù)操作的原子性(ACID)。它通過AOP代理實(shí)現(xiàn),默認(rèn)基于數(shù)據(jù)庫(kù)連接的事務(wù)傳播機(jī)制(如PROPAGATION_REQUIRED)。

環(huán)境:SpringBoot3.4.2

1. 簡(jiǎn)介

在Spring Boot開發(fā)中,@Transactional和@Async是兩個(gè)高頻使用的注解,分別服務(wù)于不同的場(chǎng)景。

@Transactional 是Spring事務(wù)管理的核心注解,用于聲明方法需要在事務(wù)上下文中執(zhí)行,確保數(shù)據(jù)庫(kù)操作的原子性(ACID)。它通過AOP代理實(shí)現(xiàn),默認(rèn)基于數(shù)據(jù)庫(kù)連接的事務(wù)傳播機(jī)制(如PROPAGATION_REQUIRED)。

@Async 是Spring異步任務(wù)的核心注解,用于將方法標(biāo)記為異步執(zhí)行,本質(zhì)是通過線程池啟動(dòng)新線程處理任務(wù),避免阻塞主線程,提升系統(tǒng)吞吐量。

當(dāng)我們寫出如下的代碼后是否存在問題呢?

@Transactional
@Async
public void processProduct(Product product) {
  this.productRepository.saveAndFlush(product) ;
  this.emailService.send() ;
}
@Service
public class EmailService {
  public void send() {
    System.err.printf("%s - 發(fā)送郵件", Thread.currentThread().getName()) ;
    System.err.println(1 / 0) ;
  }
}

EmailService#send方法中我們模擬了異常拋出,執(zhí)行上面的processProduct方法事務(wù)是正常執(zhí)行還是回滾呢?

2.問題復(fù)現(xiàn)

2.1 單元測(cè)試

通過如下單元測(cè)試:

@Resource
private ProductService productService ;
@Test
public void  testCreateProduct() {
  Product product = new Product("Spring全家桶實(shí)戰(zhàn)案例源碼", 70D);
  this.productService.processProduct(product) ;
}

數(shù)據(jù)庫(kù)初始狀態(tài)如下:

圖片圖片

執(zhí)行結(jié)果

圖片圖片

圖片

程序拋出異常后,數(shù)據(jù)庫(kù)中未插入任何數(shù)據(jù),此結(jié)果完全符合預(yù)期要求。

這一現(xiàn)象表明,在默認(rèn)配置條件下,@Transactional 與 @Async 這兩個(gè)注解能夠?qū)崿F(xiàn)良好的協(xié)同運(yùn)作,共同達(dá)成預(yù)期的業(yè)務(wù)邏輯處理效果。

2.2 錯(cuò)誤情況

我們知道@Transactional 與 @Async 2個(gè)注解底層的實(shí)現(xiàn)都是通過AOP實(shí)現(xiàn)的,那么接下來(lái),我們進(jìn)行如下的配置修改:

@Configuration
@EnableAsync(order = Ordered.HIGHEST_PRECEDENCE)
public class AsyncConfig {
}

將@EnableAsync注解的order屬性設(shè)置為最高優(yōu)先級(jí)(值越小,優(yōu)先級(jí)越高)。再次運(yùn)行上面的測(cè)試程序,執(zhí)行結(jié)果如下(先將數(shù)據(jù)庫(kù)中的數(shù)據(jù)清空):

圖片

錯(cuò)誤還是一樣的錯(cuò)誤。

圖片圖片

但是數(shù)據(jù)庫(kù)中成功插入了數(shù)據(jù),也就是事務(wù)沒有回滾。

3. 原因分析

在上述場(chǎng)景中已明確,默認(rèn)情況下 @Transactional 與 @Async 可正常協(xié)同運(yùn)作,若調(diào)整 @EnableAsync 的 order 屬性,事務(wù)會(huì)失效。

接下來(lái),我們將進(jìn)行底層原理的分析。

3.1 代理創(chuàng)建的原理

當(dāng)項(xiàng)目中引入spring-boot-starter-aop時(shí),會(huì)自動(dòng)通過@EnableAspectJAutoProxy注解開啟代理功能,其實(shí)就是注冊(cè)了一個(gè)BeanPostProcessor處理器:AnnotationAwareAspectJAutoProxyCreator。

有了處理器后還需要切面,而在Spring中定義切面的方式有2種:

  • 使用 @Aspect 聲明的高級(jí)切面
    通過該注解聲明的切面最終會(huì)被轉(zhuǎn)換為低級(jí)切面Advisor。
  • 通過實(shí)現(xiàn) Advisor 接口實(shí)現(xiàn)低級(jí)切面

總結(jié):代理對(duì)象的創(chuàng)建是通過BeanPostProcessor+Advisor實(shí)現(xiàn)。

3.2 @Transactional底層實(shí)現(xiàn)

當(dāng)我們項(xiàng)目中引入相關(guān)數(shù)據(jù)庫(kù)操作的starter時(shí),如:spring-boot-starter-data-jpa或者spring-boot-starter-data-jdbc。底層的自動(dòng)配置會(huì)通過@EnableTransactionManagement注解開啟@Transactional注解的的事務(wù)功能。

而@EnableTransactionManagement注解會(huì)自動(dòng)的注冊(cè),BeanFactoryTransactionAttributeSourceAdvisor切面。同時(shí)還會(huì)注冊(cè)InfrastructureAdvisorAutoProxyCreator處理器,但是AnnotationAwareAspectJAutoProxyCreator處理器的優(yōu)先級(jí)高于InfrastructureAdvisorAutoProxyCreator,所以最終底層最終使用的BeanPostProcessor處理將是AnnotationAwareAspectJAutoProxyCreator。

總結(jié):@Transactional事務(wù)注解將通過AnnotationAwareAspectJAutoProxyCreator + BeanFactoryTransactionAttributeSourceAdvisor創(chuàng)建代理對(duì)象。

3.3 @Async底層原理

要使用異步功能,我們需要通過@EnableAsync開啟功能,而該注解會(huì)自動(dòng)注冊(cè):AsyncAnnotationBeanPostProcessor處理器,而切面則是AsyncAnnotationAdvisor。

但是該處理器會(huì)先判斷當(dāng)前的類是不是已經(jīng)是代理對(duì)象了,如果是則只是將AsyncAnnotationAdvisor添加到當(dāng)前的切面集合中,如下源碼:

圖片圖片

那這時(shí)候是不是就是看處理@Async和@Transactional注解的處理器BeanPostProcessor誰(shuí)先執(zhí)行了?!

3.4 處理器執(zhí)行順序

AnnotationAwareAspectJAutoProxyCreator處理器默認(rèn)注冊(cè)的時(shí)候設(shè)置的優(yōu)先級(jí)是最高優(yōu)先級(jí),如下源碼:

圖片圖片

默認(rèn)情況,通過debug查看執(zhí)行順序

圖片圖片

通過這樣的執(zhí)行順序,處理@Async異步任務(wù)時(shí),這將會(huì)先開啟一個(gè)異步線程,那么后續(xù)的攔截器再執(zhí)行的時(shí)候都將會(huì)在這個(gè)異步線程中,那么這樣也就保證了事務(wù)的正確性。

當(dāng)我們通過@EnableAsync(order = Ordered.HIGHEST_PRECEDENCE)調(diào)整順序后,查看BeanPostProcessor執(zhí)行順序:

圖片圖片

當(dāng)處理 @Async 注解的處理器先執(zhí)行時(shí),會(huì)為對(duì)應(yīng) Bean 創(chuàng)建代理對(duì)象。待處理 @Transactional 注解的處理器執(zhí)行時(shí),因?qū)ο笠褳榇恚瑫?huì)基于原始類再生成代理(其 targetSource 指向 @Async 代理對(duì)象)。最終執(zhí)行業(yè)務(wù)代碼時(shí),@Transactional 代理先觸發(fā)切面邏輯(開啟事務(wù)于主線程),隨后 @Async 代理開啟異步線程。由于事務(wù)與業(yè)務(wù)操作分屬不同線程,事務(wù)無(wú)法隨異常觸發(fā)回滾。

總結(jié):默認(rèn)你不調(diào)整執(zhí)行順序那么@Transactional+@Async能很好的協(xié)同工作。

4. 新特性

從Spring 6.2起,@EnableTransactionManagement增加了一個(gè)屬性配置,可以全局控制異常回滾策略,不用再每一個(gè)@Transactional注解上進(jìn)行配置回滾策略了。

@Configuration
@EnableTransactionManagement(
  rollbackOn = RollbackOn.ALL_EXCEPTIONS)
public class TxConfig {
}

RollbackOn支持2種類型,如下:

public enum RollbackOn {
  RUNTIME_EXCEPTIONS,
  ALL_EXCEPTIONS
}


責(zé)任編輯:武曉燕 來(lái)源: Springboot全家桶實(shí)戰(zhàn)案例源碼
相關(guān)推薦

2022-09-29 13:52:55

WindowsPython代碼

2025-04-16 02:20:00

2024-03-29 08:56:47

2023-05-06 08:23:36

ChatGPT自然語(yǔ)言技術(shù)

2021-03-22 17:00:15

區(qū)塊鏈NFT數(shù)字資產(chǎn)

2021-09-08 15:02:28

人工智能AIRFID

2022-09-15 11:56:36

Javalua開發(fā)

2024-11-22 13:40:00

2012-06-01 11:19:26

2024-12-09 09:37:46

2021-07-14 10:14:25

Docker IDEA開發(fā)

2024-05-28 08:25:09

2018-10-24 16:25:57

2018-03-17 17:33:13

云計(jì)算AI人工智能

2024-04-15 12:28:00

AI模型

2025-02-17 09:20:00

AI微信模型

2024-07-22 14:09:22

@AsyncJava

2016-11-10 07:50:55

群暉云端Office

2025-02-25 10:08:38

2019-08-05 10:15:33

系統(tǒng)緩存架構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)