一文讀懂Spring中的DI和AOP


前言

Spring框架通過POJO最小侵入性編程、DI、AOP、模板代碼手段來簡化了Java 開發,簡化了企業應用的開發。POJO和模板代碼相對來說好理解,本篇重點解讀下DI和AOP。

一 DI

DI(依賴注入)定義

對象的依賴關系將由系統中負責協調各對象的第三方組件在創建對象的時候進行設定。對象無需自行的創建或管理它們的依賴關系。

背景和問題

我們行來假設沒有Spring 來管理注入依賴關系,我們是怎么來實現依賴關系管理的,直接在對象內部通過new進行創建對象,每個對象負責管理與自己相互協作的的對象(即它所依賴的對象)的引用,是程序主動去創建依賴對象。下面的一段代碼是在沒有用Spring 來實現DI的情況下,我們是怎么做的,這樣做的問題在哪?

1.高度的耦合,RecognitionServiceImpl 和ContractRepository 兩者耦合在一起。

2.難以測試,如果我們想測試RecognitionService,在不改代碼下很難來測試。

public class RecognitionServiceImpl implements RecognitionService { ContractRepository contractRepository = new ContractRepository(); } 

解決方案

通過DI,對象的依賴關系將由系統中負責協調各對象的第三方組件在創建對象的時候進行設定。對象無需自行的創建或管理它們的依賴關系。DI帶來的最大的收益是——松耦合。其次是面向接口依賴的可替換(常用的是測試的時候使用mock實現)

在Spring 框架中怎么來實現DI呢?

在Spring中創建應用組件之間的協作方式通常稱為裝配(wiring)。它提供了三種裝配實現方式,分別是XML裝配、JavaConfig裝配、自動裝配。

Spring 的裝配方式

XML裝配(在XML中顯示配置)

JavaConfig裝配(基於Java的配置 )

自動化裝配

Spring從兩個角度實現自動化裝配

組件掃描(component scanning) : Spring會自動發現應用上下文中所創建的bean 

自動裝配(autowiring) : Spring自動滿足bean之間的依賴。

 

簡單來說,DI目的只有一個就是解耦,實現代碼的松散耦合。高耦合的代碼不易測試、不易復用。

 

二、AOP

AOP的定義

AOP,面向切面編程,通過預編譯方式和運行期間動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是Spring框架中的一個重要內容,是函數式編程的一種衍生范型。利用AOP可以對業務邏輯外的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。是不是有點不太好理解。我們先有個概念上的認識,就是面向切面編程。所謂切面,橫切關注點模塊化為特殊的類,這些類被稱為切面。

背景和問題

理解AOP最關鍵的點是先理解橫切關注點,所謂的橫切關注點是指散布於應用中的多處功能。最典型的橫切關注點有日志記錄、性能統計、安全控制、事務處理、異常處理、緩存等。這些橫切的關注點從概念上與應用的業務邏輯相分離的(但是往往會直接嵌入到應用的業務邏輯中去)。沒有AOP,我們往往會把這些橫切的關注點來直接嵌入到業務邏輯中去,這樣做的一個問題是什么?一是分散在多處,就是這類的代碼分散在多處代碼中,重復出現。二是 與業務代碼耦合在一起。把這些橫切關注點與業務邏輯相分離,解耦是面向切面編程(AOP)要解決的問題。

解決方案

通過AOP 來解決橫切關注點與業務邏輯相分離解耦。橫切關注點要以描述為影響多處的功能,例如:安全就是一個橫切關注點,應用中的許多方法都會涉及到安全規則,事務也是一個橫切關注點,應用在很多方法中。

橫切關注點可以被模塊化為特殊的類,這些類被成為切面(aspect)。切面有兩個好處,一是每個關注點都集中於一個地方,二服務模塊更加的簡潔,只需主要關注於業務邏輯,像安全,事務等次要的關注點被轉移到了切面中。

AOP術語

AOP已經有了自己的一些術語。描述切面的常用術語有通知(advice)、切點(point)、和連接點。

通知(advice)

在aop 術語中,切面的工作被成為通知。通知定義了切面是什么以及何時使用。通知有兩個功能,一個是描述切面要做什么?另一個是何時做。簡單來說是在某個方法被調用之前執行,還是之后執行,還是之前之后都要執行,還是只在方法拋出異常是執行。

Spring 切面可以應用5種類型的通知。

  1. 前置通知(Before):在目標方法被調用之前調用通知功能。
  2. 后置通知(After):在目標方法完成之后調用通知,此時不關心方法的輸出是什么。
  3. 返回通知(After-returning):在目標方法成功執行之后調用通知。
  4. 異常通知(After-throwing):在目標方法拋出異常后調用。
  5. 環境通知(Around):通知包裹了被通知的方法,在被通知的方法調用之前和調用之后執行自定義的行為。

連接點(Join Point)

應用有很多的時機去應用(調用通知),這些時機被稱為連接點。連接點是應用執行過程中能夠插入切面的一個點。這個點可以是調用方法時,拋出異常時,甚至是修改一個字段時。切面代碼可以利用這些點插入到應用的正常流程之中,並添加新的行為。

切點(Poincut)

如果通知定義了切面的”什么(what)“和”何時(when)" 的話,那么切點就定義了“何出(where)"。切點的定義會匹配要織入的一個或多個連接點。通常使用明確的類和方法名稱,或者是利用正則表達式來定義所匹配的類和方法名來指定切點。

切面(Aspect)

切面是通知和切點的結合,通知和切點來定義了切面的全部內容,它是什么,在何時何處完成其功能。

引入(Introduction)

引入允許向現有的類添加新方法和屬性。

織入(Weaving)

織入是把切面應用到目標對象並創建新的代理對象的過程。切面在指定的連接點被織入到目標對象中。在目標對象的生命周期中有多個點可以織入。編譯期(AspectJ),類加載期(AspectJ5 支持),運行期(Spring Aop 以這種方式織入切面的)。

總結一下:通知包含了需要應用多個應用對象的橫切行為;連接點是程序執行過程中能夠應用通知的所有點;切點定義了通知被應用的具體位置(即哪些連接點)。簡而言之:切點定義了哪些連接點會得到通知。

Spring 對AOP的支持

創建切點來定義切面所織入的連接點是AOP框架的基本功能。

Spring 提供了4種AOP的支持。

  1. 基於代理的經典Spring Aop
  2. 純POJO切面
  3. @AspectJ 注解驅動的切面
  4. 注入式AspectJ 切面(適用於Spring 各版本)

前三種都是Spring Aop 的變體,SpringAop 構建在動態代理基礎之上(即基於動態代理),Spring對AOP的支持局限於方法攔截。

理解了這些概念后如何應用Spring AOP就相對而言來說簡單的多了。

總結

DI目的只有一個就是解耦,實現代碼的松散耦合。解決高耦合的代碼帶來的不易測試、不易復用的問題。

AOP解決的問題是如何把應用中橫切關注點與業務邏輯相分離,解耦。把之前分散在應用各處的行為放入可重用的模塊中,有效減少了代碼冗余,並讓類更關注於自身的主要功能和業務邏輯。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM