1. 程式人生 > >理解Spring(二):AOP 的概念與實現原理

理解Spring(二):AOP 的概念與實現原理

[TOC] ## 什麼是 AOP AOP(Aspect Oriented Programming,面向切面程式設計)是一種程式設計正規化,它是對 OOP(Object Oriented Programming,面向物件程式設計)的一個補充。 OOP 允許我們通過類來定義物件的屬性和行為,由於物件的行為是通過類中的方法來體現的,所以要想修改一個物件的行為,就必須修改類中相應的方法。試想這麼一個場景,我們需要對某些物件的某些行為進行耗時統計,OOP 的做法只能是挨個去修改它們所屬的類,在相應的方法上加入耗時統計的邏輯,如果只是針對少量幾個行為的修改倒也無妨,但如果要統計的是成百上千個行為呢,挨個去修改這成百上千個方法就顯得很拙劣,而且還會導致大量的程式碼重複,如果要統計的是第三方類庫中的行為,那麼 OOP 就顯得更加力不從心了。 在實際開發中,除耗時統計之外,類似的還有日誌記錄、事務控制、許可權驗證等等,它們往往穿插在各個控制流中,被各個功能模組所呼叫,但它們卻是與核心業務邏輯無關的。像這種穿插在各個功能模組中的且與核心業務無關的程式碼被稱為橫切(cross cutting)。 在傳統 OOP 中,橫切除了會導致大量的程式碼重複之外,還會使核心業務程式碼看起來臃腫,由於那些與核心業務無關的橫切程式碼同核心業務程式碼緊密耦合在一起,甚至會出現核心業務程式碼被淹沒在大量橫切程式碼之中的情況,而且這些橫切程式碼分散在系統的各個地方,非常不利於維護和管理。 AOP 提供了對橫切的處理思路,它的主要思想是,將橫切邏輯分離出來,封裝成切面,通過某種機制將其織入到指定的各個功能模組中去,而不再是同核心業務程式碼交織在一起。AOP 使得我們可以暫時忽略掉系統中的橫切邏輯,專注於核心業務邏輯的開發,實現橫切邏輯與核心業務邏輯的解耦,允許我們對橫切程式碼進行集中管理,消除程式碼重複。 ## AOP 的基本術語 切面(Aspect):是對橫切邏輯的抽象,一個切面由通知和切點兩部分組成。在實際應用中,切面被定義成一個類。 通知(Advice):是橫切邏輯的具體實現。在實際應用中,通知被定義成切面類中的一個方法,方法體內的程式碼就是橫切程式碼。通知的分類:以目標方法為參照點,根據切入方位的不同,可分為前置通知(Before)、後置通知(AfterReturning)、異常通知(AfterThrowing)、最終通知(After)與環繞通知(Around)5種。 切點(Pointcut):用於說明將通知織入到哪個方法上,它是由切點表示式來定義的。 目標物件(Target):是指那些即將織入切面的物件。這些物件中已經只剩下乾乾淨淨的核心業務邏輯的程式碼了,所有的橫切邏輯的程式碼都等待 AOP 框架的織入。 代理物件(Proxy):是指將切面應用到目標物件之後由 AOP 框架所建立的物件。可以簡單地理解為,代理物件的功能等於目標物件的核心業務邏輯功能加上橫切邏輯功能,代理物件對使用者而言是透明的。 織入(Weaving):是指將切面應用到目標物件從而建立一個新的代理物件的過程。 ## Spring AOP 的簡單應用 Spring 的 AOP 模組簡稱 Spring AOP,該模組對 AOP 提供了支援。 使用 Spring 進行面向切面程式設計的基本步驟如下: 一、定義一個切面。使用 @Aspect 註解宣告切面,並使用 @Component 註解將該 Bean 註冊到 Spring 容器。 ```java @Aspect @Component public class WebLogAspect {} ``` 二、在切面中定義一個切點。通過 @Pointcut 註解指定切點表示式。 ```java @Pointcut("execution(public * com.example.demo.controller.*.*(..))") public void controllerLog(){} ``` 三、在切面中定義一個通知。例如使用 @Before 註解定義一個前置通知,併為其指定一個切點。然後在通知的方法體內編寫橫切程式碼。 ```java @Before("controllerLog()") public void beforeAdvice(JoinPoint joinPoint){ logger.info("前置通知..."); } ``` 以上是基於註解的切面定義方式,我們會發現這些註解是由 AspectJ 提供的。AspectJ 是一個專門的 AOP 框架,它提供了比 Spring AOP 更為強大的功能。那 Spring AOP 與 AspectJ 有什麼關係呢?其實沒有什麼關係,只不過是 Spring AOP 把 AspectJ 的註解直接拿來用了罷了。所以上面這種基於註解的方式也被稱為 AspectJ 風格。 採用 AspectJ 風格來定義切面,需要開啟 AspectJ 自動代理選項,如使用註解 `@EnableAspectJAutoProxy` 或配置 XML