Spring AOP通俗理解與應用

目錄html


  關於AOP,通常都會先去了解AOP的基礎概念,如切點、通知、鏈接點、切入點 、引入和織入等,面對晦澀難懂的概念,很容易陷入困境,找了幾篇講解較好的文章,將其整合起來,幫助深刻理解AOPjava

AOP

  AOP(Aspect Oriented Programming)意爲:面向切面編程,經過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP(面向對象編程)的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容(Spring核心之一),是函數式編程的一種衍生範型。利用AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。spring

做用

  爲了更清晰的邏輯,可讓你的業務邏輯去關注本身自己的業務,而不去想一些其餘的事情,這些其餘的事情包括:安全,事物,日誌等。數據庫

圖解

傳統的縱向體系代碼複用:編程

橫向抽取機制(AOP思想):設計模式

概念

1.通知(Advice)

  就是你想要的功能,也就是上面說的 安全,事物,日誌等。你給先定義好把,而後在想用的地方用一下。緩存

2.鏈接點(JoinPoint)

  這個更好解釋了,就是spring容許你使用通知的地方,那可真就多了,基本每一個方法的前,後(二者都有也行),或拋出異常時均可以是鏈接點,spring只支持方法鏈接點.其餘如aspectJ還可讓你在構造器或屬性注入時都行,不過那不是咱關注的,只要記住,和方法有關的前先後後(拋出異常),都是鏈接點。安全

3.切入點(Pointcut)

  上面說的鏈接點的基礎上,來定義切入點,你的一個類裏,有15個方法,那就有幾十個鏈接點了對吧,可是你並不想在全部方法附近都使用通知(使用叫織入,之後再說),你只想讓其中的幾個,在調用這幾個方法以前,以後或者拋出異常時乾點什麼,那麼就用切點來定義這幾個方法,讓切點來篩選鏈接點,選中那幾個你想要的方法。app

4.切面(Aspect)

  切面是通知和切入點的結合。如今發現了吧,沒鏈接點什麼事情,鏈接點就是爲了讓你好理解切點,搞出來的,明白這個概念就好了。通知說明了幹什麼和何時幹(何時經過方法名中的before,after,around等就能知道),而切入點說明了在哪幹(指定究竟是哪一個方法),這就是一個完整的切面定義。框架

5.引入(introduction)

  容許咱們向現有的類添加新方法屬性。這不就是把切面(也就是新方法屬性:通知定義的)用到目標類中嗎

6.目標(target)

  引入中所提到的目標類,也就是要被通知的對象,也就是真正的業務邏輯,他能夠在絕不知情的狀況下,被我們織入切面。而本身專一於業務自己的邏輯。

7.代理(proxy)

  怎麼實現整套aop機制的,都是經過代理,這個一會給細說。

8.織入(weaving)

  把切面應用到目標對象來建立新的代理對象的過程。有3種方式,spring採用的是運行時,爲何是運行時,後面解釋。

  關鍵就是:切點定義了哪些鏈接點會獲得通知

通俗理解

  spring用代理類包裹切面,把他們織入到Spring管理的bean中。也就是說代理類假裝成目標類,它會截取對目標類中方法的調用,讓調用者對目標類的調用都先變成調用假裝類,假裝類中就先執行了切面,再把調用轉發給真正的目標bean。

  如今能夠本身想想,怎麼搞出來這個假裝類,纔不會被調用者發現(過JVM的檢查,JAVA是強類型檢查,哪裏都要檢查類型)。

實現和目標類相同的接口

  我也實現和你同樣的接口,反正上層都是接口級別的調用,這樣我就假裝成了和目標類同樣的類(實現了同一接口,咱是兄弟了),也就逃過了類型檢查,到java運行期的時候,利用多態的後期綁定(因此spring採用運行時),假裝類(代理類)就變成了接口的真正實現,而他裏面包裹了真實的那個目標類,最後實現具體功能的仍是目標類,只不過假裝類在以前幹了點事情(寫日誌,安全檢查,事物等)。

  這就比如,一我的讓你辦件事,每次這個時候,你弟弟就會先出來,固然他分不出來了,覺得是你,你這個弟弟雖然辦不了這事,可是他知道你能辦,因此就答應下來了,而且收了點禮物(寫日誌),收完禮物了,給把事給人家辦了啊,因此你弟弟又找你這個哥哥來了,最後把這是辦了的仍是你本身。可是你本身並不知道你弟弟已經收禮物了,你只是專心把這件事情作好。

  順着這個思路想,要是自己這個類就沒實現一個接口呢,你怎麼假裝我,我就壓根沒有機會讓你搞出這個雙胞胎的弟弟,那麼就用第2種代理方式,建立一個目標類的子類,生個兒子,讓兒子假裝我

生成子類調用

 此次用子類來作爲假裝類,固然這樣也能逃過JVM的強類型檢查,我繼承的嗎,固然查不出來了,子類重寫了目標類的全部方法,固然在這些重寫的方法中,不只實現了目標類的功能,還在這些功能以前,實現了一些其餘的(寫日誌,安全檢查,事物等)。

  此次的對比就是,兒子先從爸爸那把本事都學會了,全部人都找兒子辦事情,可是兒子每次辦和爸爸一樣的事以前,都要收點小禮物(寫日誌),而後纔去辦真正的事。固然爸爸是不知道兒子這麼幹的了。這裏就有件事情要說,某些本事是爸爸獨有的(final的),兒子學不了,學不了就辦不了這件事,辦不了這個事情,天然就不能收人家禮了。 


前一種兄弟模式,spring會使用JDK的java.lang.reflect.Proxy類,它容許Spring動態生成一個新類來實現必要的接口,織入通知,而且把對這些接口的任何調用都轉發到目標類。

  後一種父子模式,spring使用CGLIB庫生成目標類的一個子類,在建立這個子類的時候,spring織入通知,而且把對這個子類的調用委託到目標類。CGLib採用底層的字節碼技術,能夠爲一個類建立子類,在子類中採用方法攔截的技術攔截全部父類方法的調用並順勢的織入橫切邏輯。

  相比之下,仍是兄弟模式好些,他能更好的實現鬆耦合,尤爲在今天都高喊着面向接口編程的狀況下,父子模式只是在沒有實現接口的時候,也能織入通知,應當作一種例外。


實現方式

  1. JDK動態代理
  2. Cglib動態代理

應用場景

  • 記錄日誌
  • 監控方法運行時間 (監控性能)
  • 權限控制
  • 緩存優化 (第一次調用查詢數據庫,將查詢結果放入內存對象, 第二次調用, 直接從內存對象返回,不須要查詢數據庫 )
  • 事務管理 (調用方法前開啓事務, 調用方法後提交關閉事務 )

應用

  在面向對象的大環境下,咱們能夠很好地組織代碼,經過繼承、封裝和多態的思想去設計一個個比較讓人滿意的類,可是咱們慢慢的發現,咱們的代碼中逐漸多了不少重複性的代碼,有人可能會想到,把這些重複性的代碼抽取出來不就行了嗎?是這樣的,咱們看一下這種思路的一個實例:

  能夠看到,上述代碼能夠實現,可是業務代碼已經被非核心代碼所混淆,而且佔據了大量的空間!

  不只如此,假設要控制每個方法的訪問權限,只容許一部分用戶進行訪問,在不考慮過濾器的狀況下,咱們是否是須要在每個方法開始的時候判斷用戶是否具備該權限,若是有的話就能夠進行訪問,沒有,就不容許進行訪問!

  諸如此類,還有數據庫事務的控制,數據庫鏈接的建立和關閉等等,這些都充斥這大量重複性的模板代碼!一個很現實的問題,假若有一天,業務需求不須要進行日誌記錄了,那豈不是咱們須要把之前寫的代碼,所有刪掉!想一想都是一件很可怕的事情!

使用JDK動態代理

  上述爲代理類,紅色框中圈出的表示之前業務中的模板代碼,這裏直接輸出表示方法執行的過程,之前的UserServiceImpl修改成以下

測試代碼以下:

 

  執行結果能夠看出,每次調用一個方法的時候先後都會調用咱們指望的代碼,實現了咱們指望的標準!

  上述過程當中,咱們看到在動態代理的invoke方法裏邊,咱們至關於在原有方法的調用先後「植入」了咱們的通用日誌記錄代碼,若是你看到這一層的話,那麼恭喜你!你已經領悟到了AOP思想最核心的東西了!

  抽取公共代碼其實就是AOP中橫切的過程,代理對象中在方法調用先後「植入」本身寫的通用日誌記錄代碼其實就是AOP中織入的過程!這個織入的代碼也就是橫切邏輯,織入代碼的過程其實就是在原有的方法先後加強 原方法的過程!總的來講,咱們想解決咱們開發中的痛點,而後就出現了一種技術,這種技術手段就是AOP。

注意:

  咱們的代理目標對象必須實現一個接口,要是一個接口的實現類,這是由於再生成Proxy對象的時候這個方法須要一個目標對象的接口

AOP與Spring AOP的關係

  AOP是一種思想,不一樣的廠商或企業可能有不一樣的實現方式,爲了更好的應用AOP技術,技術專家們成立了AOP聯盟來探討AOP的標準化,AOP聯盟定義的AOP體系結構把與AOP相關的概念大體分爲由高到低、從使用到實現的三層關係,AOP聯盟定義的AOP體系結構以下圖:

  最後補充一下!動態代理或者設計模式重要嗎?很重要!Spring AOP用到了動態代理,Spring事務管理用到了動態代理,MyBatis數據庫鏈接池用到了動態代理,MyBatis建立Mapper用到了動態代理等等,你說重要不!要想踏進這些高層框架原理的大門,設計模式首先是咱們的第一段臺階!


本文轉發整合:

  返回頂部

相關文章
相關標籤/搜索