不要小瞧面向切面編程

本文介紹Spring AOP,面向切面編程。在權限驗證、保存日誌、安全檢查和事務控制等多個應用場景之下都會依賴該技術。以下是在自己學習過程中的一些總結,如有錯誤,還望指正。

面向切面的定義

         面向切面編程(AOP),全稱 Aspect Oriented Programming。是基於面向對象編程之上的編程思想。指的是在程序運行期間,將某段代碼動態的切入到指定方法的指定位置進行運行的編程方式。

在這裏插入圖片描述
        動態代理可以非耦合動態插入代碼,我們之前介紹過,JDK動態代理相關內容請移步:JDK動態代理

AOP的幾個術語

在這裏插入圖片描述
        切入點:我們真正需要執行日誌記錄的地方。連接點:每一個方法的每一個位置都是一個連接點。通過使用切入點表達式在衆多的連接點中選出我們感興趣的地方。

案例入門

1. 切面類
@Aspect
@Componet
public class AspectClass {
	/**
		@Before:目標方法執行之前	前置通知
		@After:目標方法結束之後	後置通知
		@AfterReturning:在目標方法正常返回之後		返回通知
		@AfterThrowing:在目標方法拋出異常之後		異常通知
		@Around:環繞通知		環繞通知
		對比動態代理:
			try{
				@Before
				method.invoke(obj,args);
				@AfterReturning
			}catch(e){
				@AfterThrowing
			}finally{
				@After
			}
		
	*/
	
	/**
		指明要切入那個類的那個方法
		execution(訪問修飾符 返回值類型 方法簽名)
	/
	@Before("execution(public String cn.joncy.MyNeedProxyClass.method1(String))")
	public static void methodStart(){
		 do something
	}
	/*
		切某個類下的所有方法 *
	*/
	@AfterReturning("execution(public String cn.joncy.MyNeedProxyClass.*(String))")
	public static void methodReturn(){
		 do something
	}
	
	@AfterThrowing("execution(public String cn.joncy.MyNeedProxyClass.*(String))")
	public static void methodException(){
		 do something
	}
	@After("execution(public String cn.joncy.MyNeedProxyClass.*(String))")
	public static void methodEnd(){
		 do something
	}
}
2. 測試執行
@Test
public void test(){
	// 此處傳入的是接口類型
	NeedProxyClass npc = ioc.getBean(NeedProxyClass.class);
	npc.method1("low");
}

AOP使用細節

1. IOC容器中保存代理對象

        從ioc容器中拿到目標對象,參數傳遞的是被代理類的接口,因爲接口是被代理類和代理類產生關聯的東西。AOP底層是動態代理,容器中保存的組件是它的代理對象(com.sun.proxy.$Proxyxxx)

2. 切入點表達式

’ * ’ :
        可以匹配一個或者多個字符:execution(public String cn.joncy.MyNeed*y.*(String ))
        匹配任意一個參數:第一個是String 第二個隨意:execution(public String cn.joncy.MyNeed*.*(String ,*))
        只能匹配一層路徑
        權限位置要麼寫上,要麼不要寫,不能寫*

‘..’:
        匹配任意多個參數,任意類型參數
        匹配任意多層路徑:execution(public String cn.*.MyNeed*.*(String ,)) 一層
                                        execution(public String cn..MyNeed*.*(String ,
)) 多層
最精確匹配:
        execution(public String cn.joncy.MyNeedProxyClass.method1(String))
最模糊匹配:
         execution(* *(..))
         execution(* *.*(..)) 雙點不能直接寫
切入點表達式還支持:&& 、|| 、!

3. 通知的執行順序

         正常執行:@Before => @After => @AfterReturning
         異常執行:@Before => @After => @AfterThrowing

4. JoinPoint的使用

        爲通知方法加上JoinPoint參數,該參數封裝了當前目標方法的詳細信息

//獲取方法參數
Object[] args = joinPoint.getArgs();
System.out.println(Arrays.asList(args));
//獲取方法名
Signature signature joinPoint.getSignature();
String name signature.getName();

         接收返回值結果和異常信息


@AfterReturning(value="execution(public String cn.joncy.MyNeedProxyClass.*(String))",returning="result")
// result的類型可以變得更加具體 比如變爲int  String等
public static void methodReturn(JoinPoint joinPoint,Object result){
	 do something
}

@AfterThrowing(value="execution(public String cn.joncy.MyNeedProxyClass.*(String))",throwing="exception")
// 此處是最大範圍的異常,可以縮小比如接數組越界或空指針異常等
public static void methodException(JoinPoint joinPoint,Exception exception){
	 do something
}

         Spring對通知方法的訪問修飾符、返回值類型要求不嚴格,唯一有要求的就是通知方法的參數,因爲Spring需要知道這些參數都是什麼,才能幫我們調用
         可以抽取可重用的切入點表達式,此表達式空實現,其他通知方法的切入點表達式的value引用該方法簽名

5. 環繞通知

         環繞通知是其他四個通知的集合體,也是最常用的通知,是最類似動態代理的通知

@Around(execution(public String cn.joncy.MyNeedProxyClass.method1(String)))
public Object myAround(ProceedingJoinPoint pjp) throws Throwable{
	Object[] args = pjg.getArgs();
	Object proceed = null;
	try{
		// 此處添加內容 == 前置通知
		proceed = pjp.proceed(args);
		// 此處添加內容 == 返回通知
	}catch(Exception e){
		// 此處添加內容 == 異常通知
		e.printStack();
	}finally{
		// 此處添加內容 == 後置通知
	}
	// 這個值決定了執行方法的最終返回值
	return proceed;
}

         環繞通知的執行順序:
環繞前置 => 普通前置 => 目標方法執行 => 環繞正常返回/出現異常(拋出異常) => 環繞後置 => 普通後置 => 普通返回/異常

6. 多切面執行順序

在這裏插入圖片描述
如圖,只需注意在一個切面中環繞前置要比普通前置先執行,環繞隻影響當前切面,所以如果切面二存在環繞通知,兩個切面都存在普通通知,執行順序爲:
切面二環繞前置=>切面二普通前置=>切面一普通前置=>目標方法=>切面一後置=>切面一返回=>切面一環繞返回=>切面一環繞後置=>切面一後置=>切面一返回

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章