AOP的基礎知識總結(jdk動態代理/cglib動態代理)
阿新 • • 發佈:2018-11-02
AOP的基礎知識總結(jdk動態代理/cglib動態代理)
知識總結
aop切面程式設計
切面:
切面包含了通知和切點,通知和切點共同定義了切面是什麼,在何時,何處執行切面邏輯。
切點:
如果說通知定義了在何時執行通知,那麼切點就定義了在何處執行通知。所以切點的作用就是
通過匹配規則查詢合適的連線點(Joinpoint),AOP 會在這些連線點上織入通知。
通知:
Spring 中對應了 5 種不同型別的通知: · 前置通知(Before):在目標方法執行前,執行通知 · 後置通知(After):在目標方法執行後,執行通知,此時不關係目標方法返回的結果是什麼 · 返回通知(After-returning):在目標方法執行後,執行通知 · 異常通知(After-throwing):在目標方法丟擲異常後執行通知 · 環繞通知(Around): 目標方法被通知包裹,通知在目標方法執行前和執行後都被會呼叫
動態代理:
Spring在選擇用JDK還是CGLiB的依據:
(1)當Bean實現介面時,Spring就會用JDK的動態代理(就比如userServiceImpl實現了IUserService)
(2)當Bean沒有實現介面時,Spring使用CGlib是實現(就比如userServiceImpl沒有實現IUserService)
(3)可以強制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)
CGlib比JDK快?
(1)使用CGLib實現動態代理,CGLib底層採用ASM位元組碼生成框架,使用位元組碼技術生成代理類,比使用Java反射效率要高。唯一需要注意的是,CGLib不能對宣告為final的方法進行代理,因為CGLib原理是動態生成被代理類的子類。 (2)在對JDK動態代理與CGlib動態代理的程式碼實驗中看,1W次執行下,JDK7及8的動態代理效能比CGlib要好20%左右。
JDK動態代理和CGLIB位元組碼生成的區別?
(1)JDK動態代理只能對實現了介面的類生成代理,而不能針對類
(2)CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法
因為是繼承,所以該類或方法最好不要宣告成final
而cglib動態代理是利用asm開源包,對代理物件類的class檔案載入進來,通過修改其位元組碼生成子類來處理。
1、如果目標物件實現了介面,預設情況下會採用JDK的動態代理實現AOP 2、如果目標物件實現了介面,可以強制使用CGLIB實現AOP 3、如果目標物件沒有實現了介面,必須採用CGLIB庫,spring會自動在JDK動態代理和CGLIB之間轉換
Spring的aop的基本的使用:
增加依賴包:
jdk:不需要額外新增依賴
cglib:
org.springframework.spring-aop.5.0.8.RELEASE
org.aspectj.aspectjweaver.1.8.13
org.springframework.spring-aspects.5.0.8.RELEASE
aopalliance.aopalliance.1.0
增加切面類類:
① 在類上使用 @Component 註解 把切面類加入到IOC容器中
② 在類上使用 @Aspect 註解 使之成為切面類
案例(可以只使用環繞通知/其他的前置後置通知之一):
@Component
@Aspect
public class LoggingAspect {
/**
* 前置通知:目標方法執行之前執行以下方法體的內容
* @param jp
*/
@Before("execution(* com.qcc.beans.aop.*.*(..))")
public void beforeMethod(JoinPoint jp){
String methodName = jp.getSignature().getName();
System.out.println("【前置通知】the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs()));
}
/**
* 返回通知:目標方法正常執行完畢時執行以下程式碼
* @param jp
* @param result
*/
@AfterReturning(value="execution(* com.qcc.beans.aop.*.*(..))",returning="result")
public void afterReturningMethod(JoinPoint jp, Object result){
String methodName = jp.getSignature().getName();
System.out.println("【返回通知】the method 【" + methodName + "】 ends with 【" + result + "】");
}
/**
* 後置通知:目標方法執行之後執行以下方法體的內容,不管是否發生異常。
* @param jp
*/
@After("execution(* com.qcc.beans.aop.*.*(..))")
public void afterMethod(JoinPoint jp){
System.out.println("【後置通知】this is a afterMethod advice...");
}
/**
* 異常通知:目標方法發生異常的時候執行以下程式碼
*/
@AfterThrowing(value="execution(* com.qcc.beans.aop.*.*(..))",throwing="e")
public void afterThorwingMethod(JoinPoint jp, NullPointerException e){
String methodName = jp.getSignature().getName();
System.out.println("【異常通知】the method 【" + methodName + "】 occurs exception: " + e);
}
// /**
// * 環繞通知:目標方法執行前後分別執行一些程式碼,發生異常的時候執行另外一些程式碼
// * @return
// */
// @Around(value="execution(* com.qcc.beans.aop.*.*(..))")
// public Object aroundMethod(ProceedingJoinPoint jp){
// String methodName = jp.getSignature().getName();
// Object result = null;
// try {
// System.out.println("【環繞通知中的--->前置通知】:the method 【" + methodName + "】 begins with " + Arrays.asList(jp.getArgs()));
// //執行目標方法
// result = jp.proceed();
// System.out.println("【環繞通知中的--->返回通知】:the method 【" + methodName + "】 ends with " + result);
// } catch (Throwable e) {
// System.out.println("【環繞通知中的--->異常通知】:the method 【" + methodName + "】 occurs exception " + e);
// }
//
// System.out.println("【環繞通知中的--->後置通知】:-----------------end.----------------------");
// return result;
// }
}