最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Spring如何使用AOP實(shí)現(xiàn)聲明式事務(wù)管理

 更新時(shí)間:2025年06月17日 10:09:29   作者:冰糖心書房  
Spring?使用?AOP?實(shí)現(xiàn)聲明式事務(wù)管理是其最強(qiáng)大的功能之一,這篇文章小編將詳細(xì)拆解這個(gè)過(guò)程,從高層概念到底層原理,感興趣的小伙伴可以參考一下

一、 核心思想:代理模式 + AOP

想象一下,你是一位非常重要的業(yè)務(wù)專家(你的 Service 層代碼),你的工作是處理核心業(yè)務(wù)邏輯(比如轉(zhuǎn)賬、更新用戶信息)。但每次處理業(yè)務(wù)前,你都需要做一些準(zhǔn)備工作(開(kāi)啟事務(wù)),結(jié)束后還要做一些收尾工作(提交或回滾事務(wù))。這些準(zhǔn)備和收尾工作繁瑣、重復(fù),而且和你的核心業(yè)務(wù)關(guān)系不大。

聲明式事務(wù)管理的目標(biāo)就是:讓你這位專家只專注于核心業(yè)務(wù),把所有繁瑣的事務(wù)管理工作交給一個(gè)“管家”來(lái)處理。

這個(gè)“管家”就是 Spring AOP 創(chuàng)建的代理對(duì)象 (Proxy)。

AOP (Aspect-Oriented Programming, 面向切面編程) 在這里扮演的角色就是:

  • 定義“切面” (Aspect):將事務(wù)管理(開(kāi)啟、提交、回滾)這個(gè)“橫切關(guān)注點(diǎn)”從業(yè)務(wù)代碼中抽離出來(lái),形成一個(gè)獨(dú)立的模塊。
  • 定義“切點(diǎn)” (Pointcut):精確地定義這個(gè)“管家”應(yīng)該在哪些方法執(zhí)行前后介入。在 Spring 中,這通常是通過(guò) @Transactional 注解來(lái)指定的。
  • 創(chuàng)建“代理” (Proxy):Spring 不會(huì)直接返回你的原始業(yè)務(wù)對(duì)象,而是會(huì)創(chuàng)建一個(gè)該對(duì)象的代理。所有對(duì)業(yè)務(wù)對(duì)象的調(diào)用都會(huì)先經(jīng)過(guò)這個(gè)代理。

二、 詳細(xì)工作流程(一步步拆解)

讓我們跟蹤一個(gè)對(duì) @Transactional 方法的調(diào)用,看看 Spring 內(nèi)部到底發(fā)生了什么。

場(chǎng)景:一個(gè) UserController 調(diào)用一個(gè) UserService 的 updateUser() 方法,該方法被 @Transactional 注解。

// 業(yè)務(wù)接口
public interface UserService {
    void updateUser(User user);
}

// 業(yè)務(wù)實(shí)現(xiàn)類
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Override
    @Transactional // <--- 關(guān)鍵注解
    public void updateUser(User user) {
        // 核心業(yè)務(wù)邏輯
        userRepository.update(user);
        // 假設(shè)這里如果出錯(cuò)了,需要回滾
        if (user.getName() == null) {
            throw new IllegalArgumentException("Username cannot be null");
        }
    }
}

// 調(diào)用方
@RestController
public class UserController {
    @Autowired
    private UserService userService; // <--- 注意:這里注入的其實(shí)是代理對(duì)象

    @PostMapping("/user/update")
    public void updateUser(@RequestBody User user) {
        userService.updateUser(user);
    }
}

階段一:應(yīng)用啟動(dòng)時(shí) - 創(chuàng)建代理

1.Bean 掃描:Spring 容器在啟動(dòng)時(shí),會(huì)掃描所有的 Bean。

2.識(shí)別 @Transactional:當(dāng) Spring 掃描到 UserServiceImpl 時(shí),它會(huì)發(fā)現(xiàn)這個(gè)類或其方法上存在 @Transactional 注解。

3.創(chuàng)建代理對(duì)象:Spring 認(rèn)識(shí)到這個(gè) Bean 需要被事務(wù)“增強(qiáng)”(Advised)。它不會(huì)直接創(chuàng)建 UserServiceImpl 的實(shí)例并放入容器,而是會(huì)通過(guò) AOP 框架為它創(chuàng)建一個(gè)代理對(duì)象。

  • 如果 UserServiceImpl 實(shí)現(xiàn)了接口(如本例中的 UserService),Spring 默認(rèn)會(huì)使用 JDK 動(dòng)態(tài)代理來(lái)創(chuàng)建一個(gè)實(shí)現(xiàn)了 UserService 接口的代理類。
  • 如果 UserServiceImpl 沒(méi)有實(shí)現(xiàn)任何接口,Spring 會(huì)使用 CGLIB 來(lái)創(chuàng)建一個(gè) UserServiceImpl 的子類作為代理。

4.注入代理:當(dāng) UserController 通過(guò) @Autowired 請(qǐng)求注入 UserService 時(shí),Spring 容器會(huì)將上一步創(chuàng)建的那個(gè)代理對(duì)象注入給它,而不是原始的 UserServiceImpl 對(duì)象。UserController 對(duì)此毫不知情,它以為自己拿到的就是普通的 UserService。

階段二:方法調(diào)用時(shí) - 事務(wù)攔截

1.調(diào)用入口:UserController 調(diào)用 userService.updateUser(user)。

2.命中代理:這個(gè)調(diào)用首先命中的是代理對(duì)象的 updateUser 方法,而不是原始對(duì)象的。

3.事務(wù)攔截器 (TransactionInterceptor) 生效:代理對(duì)象的這個(gè)方法中包含了 AOP 的“通知”(Advice)。對(duì)于事務(wù)來(lái)說(shuō),這個(gè)通知的具體實(shí)現(xiàn)就是 TransactionInterceptor。這個(gè)攔截器就像一個(gè)警衛(wèi),攔住了這次調(diào)用。

4.開(kāi)啟事務(wù):

  • TransactionInterceptor 首先會(huì)解析 updateUser 方法上 @Transactional 注解的屬性(如傳播行為 propagation、隔離級(jí)別 isolation、是否只讀 readOnly 等)。
  • 它向事務(wù)管理器 (PlatformTransactionManager) 請(qǐng)求一個(gè)事務(wù)。
  • 事務(wù)管理器(例如 DataSourceTransactionManager)會(huì)從數(shù)據(jù)庫(kù)連接池中獲取一個(gè)連接 (Connection),然后執(zhí)行 connection.setAutoCommit(false),這標(biāo)志著事務(wù)的正式開(kāi)始。

5.調(diào)用原始方法:事務(wù)成功開(kāi)啟后,TransactionInterceptor 會(huì)通過(guò)反射調(diào)用原始 UserServiceImpl 對(duì)象的 updateUser 方法。

6.執(zhí)行業(yè)務(wù)邏輯:此時(shí),才真正開(kāi)始執(zhí)行你編寫的核心業(yè)務(wù)代碼,比如 userRepository.update(user)。所有這些數(shù)據(jù)庫(kù)操作都將在上一步獲取的那個(gè)被 Spring 管理的 Connection 上執(zhí)行。

階段三:方法執(zhí)行后 - 提交或回滾

當(dāng)原始的 updateUser 方法執(zhí)行完畢后,控制權(quán)返回到 TransactionInterceptor。

1.正常執(zhí)行完畢 (沒(méi)有拋出異常):

  • 攔截器捕獲到方法正常返回。
  • 它會(huì)通知事務(wù)管理器提交 (commit) 事務(wù)。
  • 事務(wù)管理器調(diào)用 connection.commit(),將本次事務(wù)中的所有數(shù)據(jù)庫(kù)操作永久保存。

2.拋出異常:

  • updateUser 方法內(nèi)部拋出了一個(gè)異常(默認(rèn)是 RuntimeException 或 Error)。
  • 攔截器在其 try-catch 塊中捕獲到這個(gè)異常。
  • 它會(huì)通知事務(wù)管理器回滾 (rollback) 事務(wù)。
  • 事務(wù)管理器調(diào)用 connection.rollback(),撤銷本次事務(wù)中的所有數(shù)據(jù)庫(kù)操作。
  • 最后,攔截器會(huì)將捕獲到的異常繼續(xù)向上拋出,以便上層調(diào)用者(如 UserController 或全局異常處理器)能夠感知到。

階段四:收尾

無(wú)論事務(wù)是提交還是回滾,事務(wù)管理器最終都會(huì)將數(shù)據(jù)庫(kù)連接釋放回連接池。

三、 核心組件總結(jié)

@Transactional:元數(shù)據(jù),告訴 AOP 在哪里以及如何應(yīng)用事務(wù)。它本身不包含任何邏輯。

AOP 代理 (Proxy):由 Spring 動(dòng)態(tài)創(chuàng)建的“管家”對(duì)象,是事務(wù)邏輯的實(shí)際入口。它包裝了原始的業(yè)務(wù)對(duì)象。

事務(wù)攔截器 (TransactionInterceptor):AOP 通知(Advice)的具體實(shí)現(xiàn),是代理對(duì)象中的核心邏輯。它負(fù)責(zé)在目標(biāo)方法調(diào)用前后開(kāi)啟、提交或回滾事務(wù)。

事務(wù)管理器 (PlatformTransactionManager):一個(gè)統(tǒng)一的事務(wù)管理接口。Spring 通過(guò)它來(lái)適配不同的數(shù)據(jù)源技術(shù)(如 JDBC, JPA/Hibernate, JTA)。它負(fù)責(zé)執(zhí)行真正的事務(wù)操作(begin, commit, rollback)。

四、 為什么 @Transactional 在某些情況下會(huì)失效?

理解了以上原理,就很容易明白為什么 @Transactional 會(huì)在以下情況失效:

1.方法不是 public 的:CGLIB 代理是通過(guò)繼承來(lái)實(shí)現(xiàn)的,而 private 或 protected 方法無(wú)法被子類重寫(override),所以 AOP 無(wú)法攔截。JDK 動(dòng)態(tài)代理基于接口,更不存在非 public 方法。

2.同一個(gè)類中的方法調(diào)用(this 調(diào)用):

@Service
public class UserServiceImpl implements UserService {
    public void entryMethod() {
        this.updateUser(user); // <--- 失效!
    }

    @Transactional
    public void updateUser(User user) {
        // ...
    }
}

當(dāng)外部調(diào)用 entryMethod() 時(shí),進(jìn)入的是 UserServiceImpl 的原始對(duì)象(如果 entryMethod 沒(méi)有被代理)。然后 this.updateUser() 是一個(gè)對(duì)象內(nèi)部的直接調(diào)用,它繞過(guò)了代理對(duì)象,直接調(diào)用了原始對(duì)象的 updateUser 方法,因此事務(wù)攔截器根本沒(méi)有機(jī)會(huì)介入。

3.異常被 catch 掉了:

@Transactional
public void updateUser(User user) {
    try {
        // ... 拋出 RuntimeException 的代碼
    } catch (Exception e) {
        // 異常被吞了,沒(méi)有繼續(xù)拋出
    }
}

事務(wù)攔截器是通過(guò)捕獲異常來(lái)決定是否回滾的。如果異常被你的 try-catch 塊“消化”了,攔截器就認(rèn)為方法是正常執(zhí)行完畢的,于是它會(huì)提交事務(wù),而不是回滾。
通過(guò)這個(gè)機(jī)制,Spring 完美地將業(yè)務(wù)邏輯和非功能性的事務(wù)管理代碼解耦,讓開(kāi)發(fā)者能更專注于業(yè)務(wù)價(jià)值的實(shí)現(xiàn),代碼也變得更加清晰和易于維護(hù)。

到此這篇關(guān)于Spring如何使用AOP實(shí)現(xiàn)聲明式事務(wù)管理的文章就介紹到這了,更多相關(guān)Spring AOP聲明式事務(wù)管理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java?SE循環(huán)一些基本練習(xí)題總結(jié)

    Java?SE循環(huán)一些基本練習(xí)題總結(jié)

    循環(huán)語(yǔ)句可以在滿足循環(huán)條件的情況下,反復(fù)執(zhí)行某一段代碼,這段被重復(fù)執(zhí)行的代碼被稱為循環(huán)體語(yǔ)句,下面這篇文章主要給大家總結(jié)介紹了關(guān)于Java?SE循環(huán)一些基本練習(xí)題,需要的朋友可以參考下
    2024-03-03
  • SpringSecurity?鑒權(quán)與授權(quán)的具體使用

    SpringSecurity?鑒權(quán)與授權(quán)的具體使用

    本文介紹了Spring?Security在前后端分離架構(gòu)中的核心應(yīng)用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2025-07-07
  • Java Map 按key排序和按Value排序的實(shí)現(xiàn)方法

    Java Map 按key排序和按Value排序的實(shí)現(xiàn)方法

    下面小編就為大家?guī)?lái)一篇Java Map 按key排序和按Value排序的實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-08-08
  • Java中Redisson 的原理深度解析

    Java中Redisson 的原理深度解析

    Redisson是一個(gè)高性能的Redis客戶端,它通過(guò)將Redis數(shù)據(jù)結(jié)構(gòu)映射為Java對(duì)象和分布式對(duì)象,實(shí)現(xiàn)了在Java應(yīng)用中方便地使用Redis,本文給大家介紹Java中Redisson的原理,感興趣的朋友跟隨小編一起看看吧
    2025-11-11
  • Mybatis 簡(jiǎn)單啟動(dòng)過(guò)程入門詳解

    Mybatis 簡(jiǎn)單啟動(dòng)過(guò)程入門詳解

    MyBatis是一個(gè)持久層框架,簡(jiǎn)化JDBC操作,SpringBoot集成MyBatis,通過(guò)創(chuàng)建項(xiàng)目、準(zhǔn)備數(shù)據(jù)、配置文件、實(shí)體類和接口,可以實(shí)現(xiàn)數(shù)據(jù)庫(kù)操作,使用@Mapper和@Select注解簡(jiǎn)化接口實(shí)現(xiàn),測(cè)試類使用@SpringBootTest和@Test注解啟動(dòng),本文介紹Mybatis啟動(dòng)過(guò)程,感興趣的朋友一起看看吧
    2025-03-03
  • Mybatis-Plus的多數(shù)據(jù)源你了解嗎

    Mybatis-Plus的多數(shù)據(jù)源你了解嗎

    這篇文章主要為大家詳細(xì)介紹了Mybatis-Plus的多數(shù)據(jù)源,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-03-03
  • 基于springboot實(shí)現(xiàn)一個(gè)簡(jiǎn)單的aop實(shí)例

    基于springboot實(shí)現(xiàn)一個(gè)簡(jiǎn)單的aop實(shí)例

    這篇文章主要介紹了基于springboot實(shí)現(xiàn)一個(gè)簡(jiǎn)單的aop,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-11-11
  • JAVA 繼承基本類、抽象類、接口介紹

    JAVA 繼承基本類、抽象類、接口介紹

    Java是一個(gè)面向?qū)ο蟮恼Z(yǔ)言,java面向?qū)ο笠话阌腥筇卣鳎悍庋b、繼承、多態(tài)
    2013-01-01
  • JAVA音頻處理依賴庫(kù)示例操作大全(從格式轉(zhuǎn)換到音頻拼接)

    JAVA音頻處理依賴庫(kù)示例操作大全(從格式轉(zhuǎn)換到音頻拼接)

    在現(xiàn)代應(yīng)用開(kāi)發(fā)中,音頻處理是常見(jiàn)需求,包括格式轉(zhuǎn)換、音頻拼接、剪輯、降噪等操作,本教程將介紹Spring?Boot中常用的音頻處理依賴庫(kù),比較它們的特性、區(qū)別及社區(qū)活躍程度,并提供實(shí)用代碼示例,感興趣的朋友跟隨小編一起看看吧
    2025-10-10
  • Spring常用注解匯總

    Spring常用注解匯總

    這篇文章主要介紹了Spring常用注解匯總,需要的朋友可以參考下
    2014-08-08

最新評(píng)論

上思县| 兖州市| 浦城县| 屏边| 日照市| 会泽县| 项城市| 西吉县| 闽清县| 商城县| 高青县| 九江市| 涡阳县| 讷河市| 静乐县| 微博| 汾西县| 朝阳区| 延长县| 中卫市| 平武县| 太和县| 五原县| 米林县| 澄城县| 大姚县| 宣汉县| 景德镇市| 永川市| 东明县| 高平市| 玛曲县| 德格县| 商都县| 天柱县| 白山市| 哈密市| 调兵山市| 丁青县| 汝阳县| 亚东县|