讀不在三更五鼓,功只怕一曝十寒。 —— 郭沫若html
1、導言隨着軟件世界不斷複雜化,傳統的OOP(面向對象)建模思路已經不足以很好的處理好開發時所面臨的種種挑戰,AOP(Aspect Orient Programming)應運而生,它和OOP建模方式並不衝突,它是OOP編程的一種有效補充。java
OOP面向名詞領域建模,使用類做爲單位來模塊化目標系統,而AOP面向動詞領域建模,其模塊化單位則是Aspect:切面。編程
常見於處理一些具備==橫切性質==的系統級服務,例如: 日誌管理、事務管理、安全檢查緩存、對象池管理等。segmentfault
本文首先介紹了AOP主要概念和常見實現原理,後就動態代理引入SpringAOP,並簡單介紹了SpringAOP的原理和其支持的切面,最後作了一個總結。緩存
2、AOP說到AOP不得不說說代理模式。代理模式顧名思義,不直接訪問目標對象,而是經過一個代理對象來間接訪問目標對象。客戶端代碼隔着一層代理對象訪問目標對象,代理對象能夠對訪問過程作各類加工、控制。以下類圖簡潔明瞭的展現了代理模式的依賴關係、類結構。安全
ProxyPatternDemo類是客戶端代碼,Image是抽象接口,代理類ProxyImage和目標類ReadlImage都實現Image,客戶端代碼經過代理對象才能訪問目標對象。框架
常見的AOP框架都使用代理的方式給JointPoint下面會介紹添加「行爲加強」,例如日誌加強、事務加強等,具體能夠理解爲使用代理模式==透明的==的封裝了客戶端代碼對被代理對象的訪問。eclipse
Aspect對標OOP中的class的含義,封裝==橫切點的邏輯==和橫切點邏輯的==做用範圍==。切面由切入點和通知組成,它既包含了橫切邏輯的定義,也包括了切入點的定義。具體的日誌、事務、線程池管理等邏輯都在橫切邏輯的Advice(通知)下面有介紹裏實現。編程語言
PointCut是用於==限定橫切邏輯做用範圍==的謂詞限定式,其核心是切點表達式。由「切點表達式匹配JointPpoint」的概念是AOP的核心,SpringAOP 默認使用AspectJ切點表達式語言,在實際使用上有如下兩種形式,前者能夠將切入點表達式抽象出來,在SpringAOP中就是抽象成一個方法:ide
Advice封裝具體的橫切邏輯。不一樣的橫切邏輯能夠以around、before、after、return、afterThrow等形式體現。Spring AOP以攔截器的方式實現,並維護了一個以鏈接點jointPoint爲中心的攔截器鏈。
JoinPoint是橫切邏輯執行時所關聯的點。在JointPoint的「先後左右等」方向上執行Advice邏輯。PointCut表達式指定了JointPoint位置,Advice的體現形式after、before等指定了以什麼形式在JointPoint「附近」執行 。
能夠簡單的理解爲被代理對象添加方法或字段。
被代理對象。被AOP框架處理的對象
織入是建立Advice代理對象並將Aspect代理對象和業務邏輯對象鏈接起來的過程。
織入能夠在編譯時,類加載時和運行時完成。在編譯時進行織入就是靜態代理,而在運行時進行織入則是動態代理。
常見的AOP實現都是基於代理模式的,能夠分爲:1.靜態代理 2.動態代理兩種。
靜態代理: 在java領域中,最多見的基於靜態代理的實現是AspectJ(官網連接),目前已經更新到AspectJ 9了。AspectJ支持編譯時、編譯後、類加載時織入。具體原理和使用方法暫且略過不表。
動態代理: 動態代理能夠分爲基於JDK動態代理和CGLIB動態代理。都是在內存中臨時爲目標對象生成一個代理對象,客戶端代碼==透明的==經過調用代理對象提供的方法來訪問目標對象的方法。
而目前市面上最多見的基於動態代理的AOP實現是SpringAOP。
3、SpringAOPSpringAOP是Spring框架的一個關鍵組件,其是基於Spring IOC容器而實現,與IOC容器無縫銜接。SpringAOP基於動態代理、純java方式實現,不須要特定的編譯過程,也不須要關心classLoader類加載機制,本質上就是使用JDK動態代理或CGLIB來動態生成一個代理類。
當被代理對象有對應接口全部的要被代理的方法在這個接口裏都有聲明時,SpringAOP使用JDK動態代理實現,當被代理對象沒有對應的接口時,使用CGLIB實現。
CGLIB能夠理解爲繼承被代理類而動態產生一個代理對象,這和傳統的代理模式有一點區別傳統代理模式有共有接口。因爲是使用繼承機制來實現AOP,因此SpringAOP不支持對final方法的代理,更不支持對fianl類的代理,由於子類沒法對父類方法進行override(重寫)。
SpringAOP目前只支持方法級別的AOP支持,我沒有實現字段攔截,若是須要對字段訪問進行AOP處理的話,能夠考慮使用AspectJ語言。
SpringAOP不一樣於大多其餘的AOP實現,SpringAOP目標不是提供最完整的AOP實現。它的亮點在於AOP和SpringIOC之間的緊密結合(雖然已經很好用了),它非侵入的實現了AOP,不依賴於AspectJ編譯器和AspectJ織入器。在SpringAOP中使用普通的bean定義來定義Aspect,SpringAOP和AspectJ是互補的,不是非此即彼的。
下表是摘自這裏的SpringAOP和AspectJ的關鍵區別:
Spring AOP | AspectJ |
---|---|
在純 Java 中實現 | 使用 Java 編程語言的擴展實現 |
不須要單獨的編譯過程 | 除非設置 LTW,不然須要 AspectJ 編譯器 (ajc) |
只能使用運行時織入 | 運行時織入不可用。支持編譯時、編譯後和加載時織入 |
功能不強-僅支持方法級編織 | 更強大 - 能夠編織字段、方法、構造函數、靜態初始值設定項、最終類/方法等......。 |
只能在由 Spring 容器管理的 bean 上實現 | 能夠在全部域對象上實現 |
僅支持方法執行切入點 | 支持全部切入點 |
代理是由目標對象建立的, 而且切面應用在這些代理上 | 在執行應用程序以前 (在運行時) 前, 各方面直接在代碼中進行織入 |
比 AspectJ 慢多了 | 更好的性能 |
易於學習和應用 | 相對於 Spring AOP 來講更復雜 |
在JointPoint前執行的Advice,不可阻止JointPoint處方法的執行Around型能夠。使用@Before定義,具體源碼以下所示。
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface Before {String value(); // 結合SpringAOP的命名綁定機制(name binding)使用,支持的PCD有: target、this、argsString argNames() default ""; }複製代碼
附: PCD(PointCut Designators 切點指示器),是切點表達式的重要組成部分。
無論被代理方法是正常結束仍是異常結束,都會執行這個Advice。
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface After {String value(); // 同上String argNames() default ""; }複製代碼
在JointPoint後執行的Advice,當JointPoint處的方法正常執行結束後,會執行這個Advice,若是是異常執行話則不會。使用@AfterReturning定義,具體源碼以下所示。
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface AfterReturning {String value() default ""; // 和value同一個做用String pointcut() default ""; // 用於聲明返回值String returning() default ""; // 同上String argNames() default ""; }複製代碼
能夠簡單的當作帶返回值的Afteradvice。
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface AfterThrowing {String value() default "";String pointcut() default ""; // 當throwing指定的異常發生時會調用這個AdviceString throwing() default ""; // 同上String argNames() default ""; }複製代碼
正常結束不會調用這個Advice,當指定異常發生時會調用這個Advice。
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.METHOD})public @interface Around {String value(); // 同上String argNames() default ""; }// 常和ProceedingJoinPoint類結伴使用複製代碼
==最強大的Advice==,幾乎全部的其餘類型的Advice均可以使用環繞通知來實現。但Spring官方建議選用「能實現所需行爲的功能最小的通知類型」: 提供最簡單的編程模式,減小了出錯的可能性。
4、總結通常來講,咱們能夠經過手動編寫的形式來實現代理模式,但當須要被代理的對象、類==有不少==時,手動編寫的方式就不太合適,AOP就是適合這種有不少橫切點的且橫切點能夠抽象出統一邏輯的一種編程範型。
舉一個在實際生產中很常見的例子:
假如某些業務邏輯接口的調用都須要在方法開始前和方法開始以後打日誌,可使用AOP切面的方式在全部的業務邏輯方法JoinPoint處定義兩個Advice: before和after類型的。