Spring框架自誕生之日就拯救我等程序員於水火之中,它有兩大法寶,一個是IoC控制反轉,另外一個即是AOP面向切面編程。今日咱們就來破一下它的AOP法寶,以便之後也能自由使出一手AOP大法。程序員
AOP全名Aspect-oriented programming面向切面編程大法,它有不少兄弟,分別是常常見的面向對象編程,樸素的面向過程編程和神祕的函數式編程等。所謂AOP的具體解釋,以及和OOP的區別不清楚的同窗能夠自行去了解。express
AOP實現的關鍵在於AOP框架自動建立的AOP代理,AOP代理主要分爲靜態代理和動態代理。本文就主要講解AOP的基本術語,而後用一個例子讓你們完全搞懂這些名詞,最後介紹一下AOP的兩種代理方式:編程
切面是一個橫切關注點的模塊化,一個切面可以包含同一個類型的不一樣加強方法,好比說事務處理和日誌處理能夠理解爲兩個切面。切面由切入點和通知組成,它既包含了橫切邏輯的定義,也包括了切入點的定義。 Spring AOP就是負責實施切面的框架,它將切面所定義的橫切邏輯織入到切面所指定的鏈接點中。app
@Component @Aspect public class LogAspect { }
能夠簡單地認爲, 使用 @Aspect 註解的類就是切面框架
目標對象指將要被加強的對象,即包含主業務邏輯的類對象。或者說是被一個或者多個切面所通知的對象。模塊化
程序執行過程當中明確的點,如方法的調用或特定的異常被拋出。鏈接點由兩個信息肯定:函數式編程
簡單來講,鏈接點就是被攔截到的程序執行點,由於Spring只支持方法類型的鏈接點,因此在Spring中鏈接點就是被攔截到的方法。函數
@Before("pointcut()") public void log(JoinPoint joinPoint) { //這個JoinPoint參數就是鏈接點 }
切入點是對鏈接點進行攔截的條件定義。切入點表達式如何和鏈接點匹配是AOP的核心,Spring缺省使用AspectJ切入點語法。
通常認爲,全部的方法均可以認爲是鏈接點,可是咱們並不但願在全部的方法上都添加通知,而切入點的做用就是提供一組規則(使用 AspectJ pointcut expression language 來描述) 來匹配鏈接點,給知足規則的鏈接點添加通知。源碼分析
@Pointcut("execution(* com.remcarpediem.test.aop.service..*(..))") public void pointcut() { }
上邊切入點的匹配規則是com.remcarpediem.test.aop.service
包下的全部類的全部函數。ui
通知是指攔截到鏈接點以後要執行的代碼,包括了「around」、「before」和「after」等不一樣類型的通知。Spring AOP框架以攔截器來實現通知模型,並維護一個以鏈接點爲中心的攔截器鏈。
// @Before說明這是一個前置通知,log函數中是要前置執行的代碼,JoinPoint是鏈接點, @Before("pointcut()") public void log(JoinPoint joinPoint) { }
織入是將切面和業務邏輯對象鏈接起來, 並建立通知代理的過程。織入能夠在編譯時,類加載時和運行時完成。在編譯時進行織入就是靜態代理,而在運行時進行織入則是動態代理。
Advisor是切面的另一種實現,可以將通知以更爲複雜的方式織入到目標對象中,是將通知包裝爲更復雜切面的裝配器。Advisor由切入點和Advice組成。
Advisor這個概念來自於Spring對AOP的支撐,在AspectJ中是沒有等價的概念的。Advisor就像是一個小的自包含的切面,這個切面只有一個通知。切面自身經過一個Bean表示,而且必須實現一個默認接口。
// AbstractPointcutAdvisor是默認接口 public class LogAdvisor extends AbstractPointcutAdvisor { private Advice advice; // Advice private Pointcut pointcut; // 切入點 @PostConstruct public void init() { // AnnotationMatchingPointcut是依據修飾類和方法的註解進行攔截的切入點。 this.pointcut = new AnnotationMatchingPointcut((Class) null, Log.class); // 通知 this.advice = new LogMethodInterceptor(); } }
看完了上面的理論部分知識, 我相信仍是會有很多朋友感受AOP 的概念仍是很模糊, 對 AOP 的術語理解的還不是很透徹。如今咱們就找一個具體的案例來講明一下。
簡單來說,整個 aspect 能夠描述爲: 知足 pointcut 規則的 joinpoint 會被添加相應的 advice 操做。咱們來看下邊這個例子。
@Component @Aspect // 切面 public class LogAspect { private final static Logger LOGGER = LoggerFactory.getLogger(LogAspect.class.getName()); // 切入點,表達式是指com.remcarpediem.test.aop.service // 包下的全部類的全部方法 @Pointcut("execution(* com.remcarpediem.test.aop.service..*(..))") public void aspect() {} // 通知,在符合aspect切入點的方法前插入以下代碼,而且將鏈接點做爲參數傳遞 @Before("aspect()") public void log(JoinPoint joinPoint) { //鏈接點做爲參數傳入 if (LOGGER.isInfoEnabled()) { // 得到類名,方法名,參數和參數名稱。 Signature signature = joinPoint.getSignature(); String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); Object[] arguments = joinPoint.getArgs(); MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); String[] argumentNames = methodSignature.getParameterNames(); StringBuilder sb = new StringBuilder(className + "." + methodName + "("); for (int i = 0; i< arguments.length; i++) { Object argument = arguments[i]; sb.append(argumentNames[i] + "->"); sb.append(argument != null ? argument.toString() : "null "); } sb.append(")"); LOGGER.info(sb.toString()); } } }
上邊這段代碼是一個簡單的日誌相關的切面,依次定義了切入點和通知,而鏈接點做爲log的參數傳入進來,進行必定的操做,好比說獲取鏈接點函數的名稱,參數等。
所謂靜態代理就是AOP框架會在編譯階段生成AOP代理類,所以也稱爲編譯時加強。ApsectJ是靜態代理的實現之一,也是最爲流行的。靜態代理因爲在編譯時就生成了代理類,效率相比動態代理要高一些。AspectJ能夠單獨使用,也能夠和Spring結合使用。
與靜態代理不一樣,動態代理就是說AOP框架不會去修改編譯時生成的字節碼,而是在運行時在內存中生成一個AOP代理對象,這個AOP對象包含了目標對象的所有方法,而且在特定的切點作了加強處理,並回調原對象的方法。
Spring AOP中的動態代理主要有兩種方式:JDK動態代理和CGLIB動態代理。
JDK代理經過反射來處理被代理的類,而且要求被代理類必須實現一個接口。核心類是 InvocationHandler接口 和 Proxy類。
而當目標類沒有實現接口時,Spring AOP框架會使用CGLIB來動態代理目標類。
CGLIB(Code Generation Library),是一個代碼生成的類庫,能夠在運行時動態的生成某個類的子類。CGLIB是經過繼承的方式作的動態代理,所以若是某個類被標記爲final,那麼它是沒法使用CGLIB作動態代理的。核心類是 MethodInterceptor 接口和Enhancer 類
AOP的基礎知識都比較枯燥,本人也不擅長概念性的文章,不過下一篇文章就是AOP源碼分析了,但願你們能夠繼續關注。