面向對象編程(OOP)的好處是顯而易見的,缺點也一樣明顯。當須要爲多個不具備繼承關係的對象添加一個公共的方法的時候,例如日誌記錄、性能監控等,若是採用面向對象編程的方法,須要在每一個對象裏面都添加相同的方法,這樣就產生了較大的重複工做量和大量的重複代碼,不利於維護。面向切面編程(AOP)是面向對象編程的補充,簡單來講就是統一處理某一「切面」的問題的編程思想。若是使用AOP的方式進行日誌的記錄和處理,全部的日誌代碼都集中於一處,不須要再每一個方法裏面都去添加,極大減小了重複代碼。spring
通知(Advice)包含了須要用於多個應用對象的橫切行爲,徹底聽不懂,不要緊,通俗一點說就是定義了「何時」和「作什麼」。編程
鏈接點(Join Point)是程序執行過程當中可以應用通知的全部點。瀏覽器
切點(Poincut)是定義了在「什麼地方」進行切入,哪些鏈接點會獲得通知。顯然,切點必定是鏈接點。app
切面(Aspect)是通知和切點的結合。通知和切點共同定義了切面的所有內容——是什麼,什麼時候,何地完成功能。spring-boot
引入(Introduction)容許咱們向現有的類中添加新方法或者屬性。性能
織入(Weaving)是把切面應用到目標對象並建立新的代理對象的過程,分爲編譯期織入、類加載期織入和運行期織入。測試
Spring Boot使用AOP須要添加spring-boot-starter-aop依賴,以下:代理
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
不須要再添加aspectjweaver的依賴了,由於spring-boot-starter-aop包含了aspectjweaver,而且版本是較新的版本,若是在添加老版本(如1.5.4)啓動會報錯。
日誌
直接定義一個controller,代碼以下:code
@RestController public class AopController { @RequestMapping("/hello") public String sayHello(){ System.out.println("hello"); return "hello"; } }
Spring採用@AspectJ註解對POJO進行標註,該註解代表該類不只僅是一個POJO,仍是一個切面。切面是切點和通知的結合,那麼定義一個切面就須要編寫切點和通知。在代碼中,只須要添加@Aspect註解便可。
切點是經過@Pointcut註解和切點表達式定義的。
@Pointcut註解能夠在一個切面內定義可重用的切點。
因爲Spring切面粒度最小是達到方法級別,而execution表達式能夠用於明確指定方法返回類型,類名,方法名和參數名等與方法相關的部件,而且實際中,大部分須要使用AOP的業務場景也只須要達到方法級別便可,於是execution表達式的使用是最爲普遍的。如圖是execution表達式的語法:
execution表示在方法執行的時候觸發。以「」開頭,代表方法返回值類型爲任意類型。而後是全限定的類名和方法名,「」能夠表示任意類和任意方法。對於方法參數列表,可使用「..」表示參數爲任意類型。若是須要多個表達式,可使用「&&」、「||」和「!」完成與、或、非的操做。
通知有五種類型,分別是:
前置通知(@Before):在目標方法調用以前調用通知
後置通知(@After):在目標方法完成以後調用通知
環繞通知(@Around):在被通知的方法調用以前和調用以後執行自定義的方法
返回通知(@AfterReturning):在目標方法成功執行以後調用通知
異常通知(@AfterThrowing):在目標方法拋出異常以後調用通知
代碼中定義了三種類型的通知,使用@Before註解標識前置通知,打印「beforeAdvice...」,使用@After註解標識後置通知,打印「AfterAdvice...」,使用@Around註解標識環繞通知,在方法執行前和執行以後分別打印「before」和「after」。這樣一個切面就定義好了,代碼以下:
@Aspect @Component public class AopAdvice { @Pointcut("execution (* com.shangguan.aop.controller.*.*(..))") public void test() { } @Before("test()") public void beforeAdvice() { System.out.println("beforeAdvice..."); } @After("test()") public void afterAdvice() { System.out.println("afterAdvice..."); } @Around("test()") public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) { System.out.println("before"); try { proceedingJoinPoint.proceed(); } catch (Throwable t) { t.printStackTrace(); } System.out.println("after"); } }
完成以後的代碼結構如圖所示:
運行AopApplication,在瀏覽器訪問http://localhost:8080/hello,不出意外,控制檯輸出如圖所示: