為什么一個 @Transactional 注解就能開啟事務?揭秘 Spring AOP 的底層魔法

圖片

你是否也曾深陷在各種“額外”邏輯的泥潭,為了給一個核心業務方法增加日志、權限校驗或緩存,而不得不將這些非核心代碼硬塞進業務類中,導致代碼臃腫、職責不清?是時候用代理設計模式 (Proxy Design Pattern)?來解脫了!這是一種結構型設計模式,它能為你提供一個對象的替代品或占位符,以控制對這個對象的訪問。

在 Spring Boot 中,這個模式是其整個AOP(面向切面編程)框架的基石,也是實現?@Transactional@Cacheable?等神奇注解的底層秘密。它能將你的核心業務邏輯與橫切關注點(如事務、安全)優雅地分離。本文將探討為什么直接訪問對象會帶來問題,通過一個實際的權限校驗示例來展示代理模式的強大威力,并一步步揭示它在 Spring Boot 中的核心地位 —— 讓我們今天就開始解鎖 Spring AOP 的底層魔法吧!

什么是代理設計模式?🤔

代理模式的核心思想是:**為其他對象提供一種代理以控制對這個對象的訪問。**這個代理對象在客戶端和目標對象之間起到中介的作用,它可以在將請求傳遞給真實對象之前或之后,執行一些附加操作。

想象一下你點外賣,你(客戶端)通過外賣App(代理)下單,App會幫你處理支付、聯系騎手等事宜,最后才通知餐廳(真實對象)做飯。App就是餐廳的代理。

這個模式的核心組件通常包括:

  • ??抽象主題 (Subject):?定義了真實主題和代理主題的公共接口,這樣在任何使用真實主題的地方都可以使用代理主題。

  • ??真實主題 (Real Subject):?定義了代理所代表的真實實體,是最終執行業務邏輯的對象。

  • ??代理 (Proxy):?保存一個引用使得代理可以訪問實體,并實現了抽象主題接口,這樣代理就可以替代實體。它可以在調用真實主題前后執行預處理和后處理操作。

常見的代理類型有:

  • ??保護代理 (Protection Proxy):?控制對真實對象的訪問權限。

  • ??虛擬代理 (Virtual Proxy):?延遲創建昂貴的對象,直到真正需要它的時候。

  • ??遠程代理 (Remote Proxy):?為一個位于不同地址空間的對象提供一個本地的代表。

  • ??緩存代理 (Caching Proxy):?為開銷大的運算結果提供臨時存儲。

為什么要在 Spring Boot 中使用代理模式?💡

代理模式能帶來諸多好處:

  • ??控制訪問 (Access Control):?核心價值。代理可以作為“守門員”,在客戶端訪問真實對象之前進行權限檢查、狀態驗證等。

  • ??增強功能 (Enhancement):?可以在不修改真實對象代碼的前提下,為其增加額外的功能,如日志記錄、性能監控、事務管理、緩存等。

  • ??延遲加載 (Lazy Initialization):?當一個對象的創建成本很高時,虛擬代理可以等到該對象第一次被真正使用時才去創建它,從而優化應用啟動速度和資源消耗。

  • ??解耦與單一職責 (Decoupling & SRP):?將附加功能(如緩存、日志)從核心業務邏輯中分離出來,讓真實主題類更純粹,只專注于業務,符合單一職責原則。

  • ??Spring AOP 的基石 (Foundation of Spring AOP):?這是在Spring中使用代理模式最重要的原因。?Spring的AOP就是通過動態代理(JDK動態代理或CGLIB)實現的。當你使用@Transactional,?@Async,?@Cacheable或自定義切面時,Spring會自動為你創建一個代理對象來包裹你的Bean,并將相應的增強邏輯織入其中。

問題所在:混亂的非核心邏輯

假設你有一個訂單服務,其中的一個方法需要進行權限校驗。

你可能會忍不住這樣寫:

public?classOrderServiceImplimplementsOrderService?{@OverridepublicvoidcreateOrder(User user, Order order)?{// 1. 權限校驗邏輯硬編碼在業務方法中if?(!"ADMIN".equals(user.getRole())) {thrownewSecurityException("只有管理員才能創建訂單!");}// 2. 核心業務邏輯System.out.println("訂單創建成功!");// ... 保存訂單到數據庫}
}

這種代碼的問題在于:

??違反單一職責原則:?OrderService?不僅要負責訂單業務,還要負責權限校驗。
??代碼重復:?如果其他方法也需要同樣的權限校驗,你就必須復制粘貼這段if代碼。
??維護困難:?如果權限邏輯發生變化(例如,VIP用戶也可以創建訂單),你需要修改所有相關的業務方法。

??代理模式來修復
我們可以創建一個?OrderServiceProxy,它和真實的?OrderServiceImpl?實現同一個接口。客戶端通過代理訪問,代理在調用真實方法前,先完成權限校驗。

一步步實現 Java 示例:圖像查看器權限代理

第一步:定義抽象主題接口

public?interface?ImageViewer?{void?viewImage(String imageName);
}

第二步:創建真實主題

public?class?RealImageViewer?implements?ImageViewer?{@Overridepublic?void?viewImage(String imageName)?{System.out.println("正在顯示圖片: "?+ imageName);}
}

第三步:創建保護代理

public?classSecureImageViewerProxyimplementsImageViewer?{private?ImageViewer realViewer;private?User user;publicSecureImageViewerProxy(User user)?{this.realViewer =?newRealImageViewer();this.user = user;}@OverridepublicvoidviewImage(String imageName)?{// 在調用真實對象前,執行權限檢查if?(user.hasViewPermission()) {System.out.println("【代理】權限校驗通過。");realViewer.viewImage(imageName);}?else?{System.out.println("【代理】抱歉,"?+ user.getName() +?",你沒有權限查看圖片。");}}
}
// User類和hasViewPermission()方法此處省略

第四步:客戶端使用

public?classMain?{publicstaticvoidmain(String[] args)?{Useradmin=newUser("Admin",?true);Userguest=newUser("Guest",?false);ImageViewerviewerForAdmin=newSecureImageViewerProxy(admin);viewerForAdmin.viewImage("secret.jpg");?// 可以查看ImageViewerviewerForGuest=newSecureImageViewerProxy(guest);viewerForGuest.viewImage("secret.jpg");?// 沒有權限}
}
Spring Boot 應用案例:揭秘@Transactional的魔法

在 Spring Boot 中,我們幾乎從不手動創建代理。框架為我們代勞了一切。讓我們看看?@Transactional?是如何工作的。

第一步:定義一個普通的業務服務(真實主題)

import?org.springframework.stereotype.Service;publicinterfaceUserService?{voidcreateUser(String name);
}@Service
publicclassUserServiceImplimplementsUserService?{@OverridepublicvoidcreateUser(String name)?{System.out.println("正在將用戶 "?+ name +?" 保存到數據庫...(核心業務邏輯)");// 此處沒有一行事務相關的代碼!}
}

第二步:通過注解“請求”代理功能
我們只需要在需要事務的方法上,加上?@Transactional?注解。

@Service
public?class?UserServiceImpl?implements?UserService?{@Override@Transactional?// 請求Spring為這個方法提供事務管理public?void?createUser(String name)?{System.out.println("正在將用戶 "?+ name +?" 保存到數據庫...(核心業務邏輯)");// 如果這里發生異常,事務會自動回滾}
}

第三步:在客戶端中注入并驗證

import?org.springframework.boot.CommandLineRunner;
import?org.springframework.stereotype.Component;@Component
publicclassAppRunnerimplementsCommandLineRunner?{privatefinal?UserService userService;publicAppRunner(UserService userService)?{this.userService = userService;}@Overridepublicvoidrun(String... args)throws?Exception {System.out.println("注入的UserService類型: "?+ userService.getClass().getName());userService.createUser("Alice");}
}

啟動應用,查看控制臺輸出:

注入的UserService類型: com.example.proxy.UserServiceImpl$$SpringCGLIB$$0
正在將用戶 Alice 保存到數據庫...(核心業務邏輯)

揭秘時刻:
userService.getClass().getName()?的輸出不是?UserServiceImpl,而是一個帶有?$$SpringCGLIB$$?后綴的類。這證明了Spring容器注入給我們的,根本不是原始的?UserServiceImpl?對象,而是由Spring在運行時動態創建的一個代理對象

正是這個代理對象,在調用我們真正的?createUser?方法之前,開啟了數據庫事務;在方法執行之后,提交或回滾了事務。這就是代理模式在Spring中的威力。

代理模式 vs. 裝飾器模式
  • ??意圖不同:?代理模式的核心是控制對對象的訪問,它可以決定是否將請求轉發給真實對象。裝飾器模式的核心是增強對象的功能,它一定會執行真實對象的方法,并在此基礎上增加新職責。

  • ??關注點:?代理模式關注的是“訪問控制”和“隱藏”。裝飾器模式關注的是“功能疊加”。

? 何時使用代理模式
  • ? 當你想為一個對象提供一個替代品或占位符,以控制對它的訪問時(如懶加載、權限控制)。

  • ? 當你想在不修改對象代碼的前提下,為其增加一些通用的、橫切性的功能時(如日志、事務、緩存)。

  • ??在Spring中:?當你使用AOP相關的所有功能時,你其實都在隱式地使用代理模式。

🚫 何時不宜使用代理模式
  • ? 當一個調用關系非常簡單,不需要任何形式的訪問控制或功能增強時,引入代理會增加不必要的復雜性。

  • ? 當對性能要求極高,無法承受代理帶來的微小性能開銷時(通常可以忽略不計)。

🏁 總結

代理設計模式是面向對象編程中一個極其重要和強大的模式。它通過引入一個“替身”或“中介”,優雅地實現了對真實對象的訪問控制和功能增強,是實現系統解耦和職責分離的關鍵。

在現代化的 Spring Boot 開發中,代理模式已經不再需要我們手動編寫,而是升華為框架的核心基石。Spring AOP 通過動態代理技術,將開發者從繁瑣的事務管理、日志記錄等橫切關注點中解放出來,讓我們只需一個簡單的注解,就能享受到代理模式帶來的巨大威力。這使得我們的系統:

  • ??業務邏輯更純粹

  • ??代碼更簡潔,配置化更強

  • ??易于維護和測試

理解代理模式的本質,就是理解Spring AOP核心魔法的關鍵。掌握它,你才能真正洞悉Spring框架的優雅設計,并編寫出更高質量的企業級應用。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/914141.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/914141.shtml
英文地址,請注明出處:http://en.pswp.cn/news/914141.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

《Spring 中上下文傳遞的那些事兒》Part 8:構建統一上下文框架設計與實現(實戰篇)

📝 Part 8:構建統一上下文框架設計與實現(實戰篇) 在實際項目中,我們往往需要處理多種上下文來源,例如: Web 請求上下文(RequestContextHolder)日志追蹤上下文&#xf…

配置驅動開發:初探零代碼構建嵌入式軟件配置工具

前言在嵌入式軟件開發中,硬件初始化與寄存器配置長期依賴人工編寫重復代碼。以STM32外設初始化為例,開發者需手動完成時鐘使能、引腳模式設置、參數配置等步驟,不僅耗時易錯(如位掩碼寫反、模式枚舉值混淆)&#xff0c…

Elasticsearch混合搜索深度解析(下):執行機制與完整流程

引言 在上篇中,我們發現了KNN結果通過SubSearch機制被保留的關鍵事實。本篇將繼續深入分析混合搜索的執行機制,揭示完整的處理流程,并解答之前的所有疑惑。 深入源碼分析 1. SubSearch的執行機制 1.1 KnnScoreDocQueryBuilder的實現 KNN結果被…

Apache HTTP Server 從安裝到配置

一、Apache 是什么?Apache(全稱 Apache HTTP Server)是當前最流行的開源Web服務器軟件之一,由Apache軟件基金會維護。它以穩定性高、模塊化設計和靈活的配置著稱,支持Linux、Windows等多平臺,是搭建個人博客…

php中調用對象的方法可以使用array($object, ‘methodName‘)?

是的,在PHP中,array($object, methodName) 是一種標準的回調語法,用于表示“調用某個對象的特定方法”。這種語法可以被許多函數(如 call_user_func()、call_user_func_array()、usort() 等)識別并執行。 語法原理 在P…

【設計模式】單例模式 餓漢式單例與懶漢式單例

單例模式(Singleton Pattern)詳解一、單例模式簡介 單例模式(Singleton Pattern) 是一種 創建型設計模式,它確保一個類只有一個實例,并提供一個全局訪問點來獲取這個實例。(對象創建型模式&…

vue3 el-table 行數據沾滿格自動換行

在使用 Vue 3 結合 Element Plus 的 <el-table> 組件時&#xff0c;如果你希望當表格中的行數據文本過長時能夠自動換行&#xff0c;而不是溢出到其他單元格或簡單地截斷&#xff0c;你可以通過以下幾種方式來實現&#xff1a;方法 1&#xff1a;使用 CSS最簡單的方法是通…

windows電腦遠程win系統服務器上的wsl2

情況 我自己使用win11筆記本電腦&#xff0c;想要遠程win11服務器上的wsl2 我這里只有服務器安裝了wsl2&#xff0c;win11筆記本沒有安裝 因此下面提到的Ubuntu終端指的是win服務器上的wsl2終端 一定要區分是在哪里輸入命令&#xff01;&#xff01; 安裝SSH 在服務器上&#x…

神經輻射場 (NeRF):重構三維世界的AI新視角

神經輻射場 (NeRF)&#xff1a;重構三維世界的AI新視角 舊金山蜿蜒起伏的街道上&#xff0c;一輛裝備12個攝像頭的Waymo自動駕駛測試車緩緩駛過。它記錄的280萬張街景圖像并未被簡單地拼接成平面地圖&#xff0c;而是被輸入一個名為Block-NeRF的神經網絡。數周后&#xff0c;一…

Kubernetes自動擴縮容方案對比與實踐指南

Kubernetes自動擴縮容方案對比與實踐指南 隨著微服務架構和容器化的廣泛采用&#xff0c;Kubernetes 自動擴縮容&#xff08;Autoscaling&#xff09;成為保障生產環境性能穩定與資源高效利用的關鍵技術。面對水平 Pod 擴縮容、垂直資源調整、集群節點擴縮容以及事件驅動擴縮容…

【CVPR2025】計算機視覺|SIREN: 元學習賦能!突破INR高分辨率圖像分類難題

論文地址&#xff1a;https://arxiv.org/pdf/2503.18123v1 代碼地址&#xff1a;https://github.com/SanderGielisse/MWT 關注UP CV縫合怪&#xff0c;分享最計算機視覺新即插即用模塊&#xff0c;并提供配套的論文資料與代碼。 https://space.bilibili.com/473764881 摘要 …

牛客周賽 Round 99

賽時成績如下&#xff1a;A. Round 99題目描述 對于給定的五位整數&#xff0c;檢查其中是否含有數字 99&#xff1b;換句話說&#xff0c;檢查是否存在相鄰的兩個數位&#xff0c;其值均為 。解題思路&#xff1a; 檢查相鄰的兩個數字是否均為9#include <bits/stdc.h> u…

從0到1搭建個人技術博客:用GitHub Pages+Hexo實現

一、為什么要搭建個人技術博客&#xff1f; 在技術圈&#xff0c;擁有個人博客的好處不言而喻&#xff1a; 簡歷加分項&#xff1a;面試官更青睞有技術沉淀的候選人知識系統化&#xff1a;輸出倒逼輸入&#xff0c;加深技術理解人脈拓展&#xff1a;吸引同行關注&#xff0c;…

Ubuntu22.04 設置顯示存在雙屏卻無法雙屏顯示

文章目錄一、背景描述二、解決方法一、背景描述 回到工位后&#xff0c;發現昨天離開時還可正常顯示的雙屏&#xff0c;今早ubuntu22.04 的設置界面顯示有雙屏&#xff0c;但外接的顯示屏無法正常顯示。 首先&#xff0c;查看當前圖像處理顯卡是否為N卡&#xff0c;沒錯&#…

高亞科技簽約奕源金屬,助力打造高效智能化采購管理體系

深圳市奕源金屬制品有限公司近日&#xff0c;國內企業管理軟件服務商高亞科技與深圳市奕源金屬制品有限公司&#xff08;以下簡稱“奕源金屬”&#xff09;正式簽約&#xff0c;雙方將基于高亞科技自主研發的8Manage SRM采購管理系統&#xff0c;共同推動奕源金屬采購管理的數字…

數據結構之map

map的基本介紹我們常常把map稱之為映射&#xff0c;就是將一個元素&#xff08;通常稱之為key鍵&#xff09;與一個相對應的值&#xff08;通常稱之為value&#xff09;關聯起來&#xff0c;比如說一個學生的名字&#xff08;key&#xff09;有與之對應的成績&#xff08;value…

vue3 canvas 選擇器 Canvas 增加頁面性能

文章目錄Vue3 選擇器 Canvas 增加頁面性能基于Vue3 Composition API和Canvas實現的交互式選擇器&#xff0c;支持PC端和移動端的拖動選擇、多選取消選擇功能vue3組件封裝html代碼Vue3 選擇器 Canvas 增加頁面性能 基于Vue3 Composition API和Canvas實現的交互式選擇器&#xf…

Python 實戰:打造多文件批量重命名工具

引言在實際運維、測試、數據分析、開發流程中&#xff0c;我們經常會處理成百上千條命令操作&#xff0c;例如&#xff1a;各種腳本任務&#xff08;啟動、備份、重啟、日志查看&#xff09;數據處理流程&#xff08;爬取 → 清洗 → 統計 → 可視化&#xff09;配置自動化&…

設計模式筆記_結構型_代理模式

1. 代理模式介紹代理模式是一種結構型設計模式&#xff0c;它允許你提供一個代理對象來控制對另一個對象的訪問。代理對象通常在客戶端和目標對象之間起到中介作用&#xff0c;能夠在不改變目標對象的前提下增加額外的功能操作&#xff0c;比如延遲初始化、訪問控制、日志記錄等…

C語言<數據結構-單鏈表>(收尾)

上篇博客我將基礎的尾插、尾刪、頭插、頭刪逐一講解了&#xff0c;這篇博客將對上篇博客進行收尾&#xff0c;講一下指定位置操作增刪以及查找這幾個函數&#xff0c;其實大同小異&#xff1a;一.查找函數&#xff1a;查找函數其實就是一個簡單的循環遍歷&#xff0c;所以不加以…