面向切面編程(AOP)
1.AOP的簡單理解
AOP全稱:Aspect Oriented Programming;
面向切面編程是通過預編譯方式和運行期動態代理的方式實現程序功能的統一維護的一種技術;
面向切面編程其實是淩駕於系統之上的一種設計思想,該思想不影響原始方法的調用;
能夠進入切面中的必須與切入點表達式匹配,只有匹配才能進入切面中執行通知方法(特定的業務邏輯);
面向切面編程的思想是彌補了面向對象的不足;擴展性特別的好;並且多個切面互不影響。
2.AOP和OOP兩種思想
OOP(面向對象編程)是將一段業務處理過程的實體及其行為和屬性進行抽象封裝。從而可以實現單元劃分和代碼重用,以便提高效率。
AOP(面向切面編程)是針對某一個特定的業務處理過程進行抽取形成一個切面。也就是說AOP處理的是某個階段或者說業務處理時的某個步驟。可以極大的降低模塊之間的耦合性和達到了很好的隔離效果。
通俗點理解,OOP思想面對的是一個對象,而AOP面對的是業務的某個階段或步驟;如果幾個或更多個的業務邏輯過程中,有重復的操作行為,就可以使用AOP提取出來,運用動態代理(JDK或CGLib),實現程序功能的統一維護。就比如說需要事務,日誌,權限等這些共有的特定業務邏輯時,需要將這些特定的業務邏輯進行抽取形成切面。若使用OOP思想解決這類的問題就得在哪需要這段特定的業務邏輯就得摻雜到處理普通業務邏輯的代碼中。這樣就會有很多代碼重復,並不高效,而AOP在這一點上高效的幫OOP解決了繁瑣的代碼重復問題。
3.AOP 專業用詞:
切面(Aspect):一個關註點的模塊化,這個關註點可能會橫切多個對象。--------- > 共用的一個類
切入點(PointCut):匹配連接點的斷言。 -------> 一種匹配規則,只有滿足規則就可執行切面中的通知
通知(Advice):在切面的某個特定的連接點上執行的動作。------> 切面中的方法(主要實現額外操作)
連接點(JoinPoint):在程序執行過程中某個特定的點。-------> 由代理對象調用的那個方法
目標對象(Target Object):被一個或者多個切面所通知的對象。----->真實對象
Spring 中 AOP 代理由 Spring 的 IOC 容器負責生成、管理,其依賴關系也由 IOC 容器負責管理。
4.SpringAOP的實現原理
1)生成代理對象規則
如果被代理者實現了接口,則使用JDK的動態代理為其創建代理對象,如果沒有實現接口,則使用cglib為其創建代理對象。
2)人為修改使用CGLib創建代理對象
<aop:config poxy-target-class="true" >
.....
</aop:config>
3)AOP調用過程
當用戶獲取對象時,首先會與切入點做匹配,如果匹配成功則為其創建代理對象;當代理對象調用方法時即連接點,就會調用與切入點綁定的通知方法。
5.五大通知
- 前置通知(Before advice):在目標方法執行之前執行
- 後置通知(After returning advice):在目標方法執行之後 執行
- 異常通知(After throwing advice):目標方法拋出異常後 執行
- 最終通知(After (finally) advice):在目標方法執行之後都會執行
- 環繞通知(Around Advice):在目標方法執行之前執行,執行之後執行
前四大通知都不能管理目標方法是否執行。也就是說跟目標方法不搭邊
ProceedingJoinPoint 只能環繞通知中
註意: 若多個環繞通知同時執行時,會形成一種嵌套關系。反正最終代理對象調用的連接點只能執行一次不可能執行多次。
6.Spring中使用AOP通知時參數的使用
這是一個後置通知的配置:
throwing="...." 傳的是一個異常對象
<aop:after-throwing method="afterThrow" pointcut-ref="pc" throwing="throwable"/>
其它幾大通知類似配置即可。
在執行環繞通知中,若傳入了一個對象,這時環繞通知有兩個參數,ProceedingJoinPoint 參數必須是放置第一位,不然報錯。切記切記!!
1 //環繞通知 2 public Object around(ProceedingJoinPoint point,Transcation tx){ 3 4 //執行連接點 5 point.proceed(); 6 7 8 }
7.AOP註解形式
[email protected] 標識在類上,標識該類是個切面類
[email protected] 前置通知
[email protected] 後置通知
[email protected] 異常通知
[email protected] 最終通知
[email protected] 環繞通知
1)開啟註解
1 <!-- 開啟AOP切面註解 --> 2 <aop:aspectj-autoproxy/>
2)配置切面
1 @Component 2 @Aspect 3 public class aspectclass { 4 //定義一個切入點 5 @Pointcut("execution(* Servlet..*.*(..))") 6 public void pointCut(){} 7 8 @Around("pointCut()")[email protected]("execution(* Servlet..*.*(..))") 9 public Object around(ProceedingJoinPoint point) throws Throwable{ 10 System.out.println("我是環繞通知。。。"); 11 System.out.println("目標類的名稱:"+point.getTarget().getClass()); 12 System.out.println("方法的名稱:"+point.getSignature().getName()); 13 long c1 = System.currentTimeMillis(); 14 Object proceed = point.proceed(); 15 long c2 = System.currentTimeMillis(); 16 System.out.println("方法執行的時間:"+(c2-c1)); 17 return proceed; 18 } 19 20 @AfterThrowing(value="pointCut()",throwing="e")[email protected](value="execution(* Servlet..*.*(..))",thowing="e") 21 public void exce(JoinPoint point,Throwable e){ 22 System.out.println("異常類:"+e.getClass()); 23 System.out.println("異常信息:"+e.getMessage()); 24 System.out.println("出現異常的方法:"+point.getSignature().getName()); 25 System.out.println("目標類:"+point.getTarget().getClass()); 26 } 27 28 }
8.切入點表達式匹配註解
1 @Around("execution(* Servlet..*.*(..)) && @annotation(ann)") 2 public Object aroundTx(ProceedingJoinPoint point,Transactional ann) throws Throwable{ 3 tx.begin(); 4 Object proceed = point.proceed(); 5 tx.commit(); 6 return proceed; 7 }
在進行匹配時,首先會匹配滿足切入點表達式且同時含有註解的方法,之後再將註解類型進行匹配,如果匹配成功則執行通知方法,如果註解類型不匹配,則整個切入點不匹配,不執行通知方法。
面向切面編程(AOP)