idiyu AOP
簡介
AOP(Aspect Orient Programming),也就是面向切面編程,作爲面向對象編程的補充。面向切面編程(AOP)和麪向對象編程(OOP)互爲補充,面向對象編程將程序分解成各個層次的對象,而面向切面編程將程序運行過程分解成各個切面,可以這樣理解,面向對象編程從靜態角度考慮程序結構,面向切面編程是從動態角度考慮運行過程。AOP專門用來處理具有橫切性質的系統服務,如事務管理、安全檢查、緩存、對象池管理等。
基本概念
切面(Aspect):業務流程運行的某個特定步驟,也就是應用運行過程中的關注點,關注點可以橫切多個對象,所以常常也稱爲橫切關注點。
連接點(Joinpoint):程序運行過程中明確的點,如方法的調用,或者異常的拋出。Spring AOP中,連接點總是方法的調用。
增強處理(Advice):AOP框架在特定在特定的切入點執行的增強處理。出來有”around”、”before”、”after”等類型。
切入點(Pointcut):可以插入增強處理的連接點。簡而言之,當某個連接點滿足指定要求時,該連接點將被添加增強處理,該連接點也就變成了切入點。
引入:將方法或者字段添加到被處理的類中。Spring允許引入新的接口到任何被處理的對象。
目標對象(Target):被AOP框架進行增強處理的對象,也被稱爲增強的對象。如果AOP框架是通過運行時代理來實現的,那麼這個對象是一個被代理的對象。
AOP代理:AOP框架創建的對象,簡單地說代理就是對目標對象的加強。Spring中的AOP代理可以是JDK動態代理,也可以是DBLIB代理。前者爲實現接口的目標對象的代理,後者爲不實現接口的目標對象的代理。
織入(Weaving):將增強處理添加到目標對象中,並創建一個被增強的對象(AOP代理)的過程就是織入。織入有兩種方式:編譯時增強(AspectJ)和運行時增強(DGLIB)。
AOP代理實際是由AOP框架動態生成的一個對象,該對象包含了目標對象全部的方法。AOP的方法在特定的切入點添加增強處理,並回調了目標對象的方法。
幾種常用增強處理
Before增強處理
Before增強處理使用@Before來標註一個方法,需要指定一個value屬性,該屬性指定一個切入點表達式(既可以是一個已有的切入點,也可以直接定義切入點表達式),用於指定該增強處理將被織入哪些切入點。
實例介紹:
//定義一個Before增強處理的切面
@Aspect
public class BeforeAdviceTest {
//執行lee包下所有類的所有方法都將作爲切入點
@Before("execution(* kuozhan.before.*.*(..))")
public void authority(){
System.out.println("模擬執行權限檢查");
}
}
//lee類下定義一個Chiness類,該類使用@Component Annotation進行標註
@Component
public class Chiness implements Person {
//實現Person接口的sayHello方法
public String sayHello(String name) {
return name+" hello,Spring AOP!";
}
//實現Person接口的eat方法
public void eat(String food) {
System.out.println("我正在吃:"+food);
}
public static void main(String[] args){
Chiness ch=new Chiness();
System.out.println(ch.sayHello("luowz"));
ch.eat("pie");
}
}
使用Before增強處理只能在目標方法執行之前織入增強,使用Before增強處理無需理會目標方法的執行,所以Before處理無法阻擊目標方法的執行。
AfterReturning增強處理
After增強處理使用@AfterReturning來標註一個AfterReturning增強處理,目標方法完成後將被織入。需要指定的兩個常用的屬性:
pointcut/value:兩個屬性作用是一樣的,用於指定切入點對應的切入表達式。當指定pointcut屬性值後,value屬性值將被覆蓋。
returning:指定一個返回值形參名,增強處理定義的方法可通過該形參名來訪問目標方法的返回值。
@Aspect
public class AfterReturningTest {
//執行lee包下所有類的所有方法都將作爲切入點
@AfterReturning(pointcut="execttion(* kuozhan.after.*.*(..))",returning="rvt")
public void log(Object rvt){
System.out.println("獲取目標對象方法返回值:"+rvt);
System.out.println("模擬日記積累功能。。。");
}
}
使用returning屬性可用於標註一個額外的作用:它可用於限定切入點只匹配具有對應返回值類型的方法。如上例中log()方法中定義rvt形參的類型爲String,則該切入點只匹配lee包下返回值類型爲String的 所有方法。
AfterThrowing增強處理
AfterThrowing增強處理使用@AfterThrowing標註一個方法,主要用於處理程序中未處理的異常。常用屬性有:
pointcut/value:指定切入點對應的切入表達式
throwing:指定一個返回值形參名,增強處理定義的方法可通過該形參名來訪問目標方法中所拋出的異常對象
//定義一個切面
@Aspect
public class AfterThrowingAdviceTest
{
//執行lee包下所有類的、所有方法都將作爲切入點
@AfterThrowing(pointcut="execution(* lee.*.*(..))"
, throwing="ex")
public void doRecoveryActions(Throwable ex)
{
System.out.println("目標方法中拋出的異常:" + ex);
System.out.println("模擬拋出異常後的增強處理...");
}
}
@Component
public class Chinese implements Person
{
//實現Person接口的sayHello()方法
public String sayHello(String name)
{
//該方法體內雖然拋出了異常,但該方法
//自己處理了該異常,所以AOP不會對該異常進行處理
try
{
System.out.println("sayHello方法開始被執行...");
new FileInputStream("a.txt");
}
catch (Exception ex)
{
System.out.println("目標類的異常處理"
+ ex.getMessage());
}
//返回簡單的字符串
return name + " Hello , Spring AOP";
}
//定義一個divide()方法
public void divide()
{
int a = 5 / 0;
System.out.println("divide執行完成!");
}
}
sayHello()方法和divide()方法都會拋出異常,但sayHello()方法已經用catch語句顯示地處理,Spring AOP不會處理該異常;而divide()方法將拋出一個ArithmeticExceptionyic ,且沒有任何程序處理,故Spring AOP將會對該異常進行處理。
注意:throwing屬性還用於限定切入點只匹配指定類型的異常。如doRecoveryActions()方法中定義的ex形參的類型是NullPointerException,則該切入點只匹配拋出類型爲NullPointerException的異常情況。catch捕捉到的異常會完成處理該異常,若catch塊中沒有重新拋出新異常,則該方法可以正常結束;而AfterThrowing處理異常雖然處理了該異常,但它不能完成處理該異常,還會傳播到上一級調用者。
After增強處理
After增強處理用@After進行標註,不論方法如何結束(正常的或拋異常的),After都會被織入目標對象中,因此After必須準備正常返回和異常返回兩種情況,這種增強適合處理釋放資源。需要指定一個屬性value,指定增強處理被織入的切入點。
//定義一個切面
@Aspect
public class AfterAdviceTest
{
//執行lee包下所有類的、所有方法都將作爲切入點
@After("execution(* lee.*.*(..))")
public void release()
{
System.out.println("模擬方法結束後的釋放資源...");
}
}
Around增強處理
Around增強處理使用@Around標註一個方法,它相當Before增強處理和After增強處理的總和,既可以在執行目標方法是織入增強處理,也可以執行目標方法之後織入增強處理。Around增強處理可以決定目標方法什麼時候執行,如何執行,甚至可以完全阻止目標方法的執行,還可以改變執行目標方法的參數值,改變執行目標方法之後的返回值。Around是功能最強大的增強處理,但通常需要在線程安全的環境下使用,消耗資源比較大。Around需要指定一個屬性value,指定增強處理被織入的切入點。
定義一個Around增強處理第一個形參必須是ProceedingJoinPoint類型(至少一個形參),在增強處理方法體內,調用ProceedingJoinPoint的proceed()方法纔會執行目標方法—這是Around增強處理可以完全控制目標對象執行的時機、如何執行的關鍵;如果程序沒有調用,目標方法不會被執行,proceed()方法執行時,可以傳入一個Object[]對象,該數組的值將被傳如目標方法作爲執行的實參。
//定義一個切面
@Aspect
public class AroundAdviceTest
{
//執行lee包下所有類的、所有方法都將作爲切入點
@Around("execution(* lee.*.*(..))")
public Object processTx(ProceedingJoinPoint jp)
throws java.lang.Throwable
{
System.out.println("執行目標方法之前,模擬開始事務...");
//執行目標方法,並保存目標方法執行後的返回值
Object rvt = jp.proceed(new String[]{"被改變的參數"});
System.out.println("執行目標方法之後,模擬結束事務...");
return rvt + " 新增的內容";
}
}
當調用ProceedingJoinPoint的proceed()方法時,傳入的Object[]參數值將作爲目標方法的參數,如果傳入的Object[]數字長度與目標方法所需的參數個數不相等,或者參數的類型不一致,程序將會拋出異常。