2. AOP基礎
學習完spring的事務管理之后,接下來我們進入到AOP的學習。 AOP也是spring框架的第二大核心,我們先來學習AOP的基礎。
在AOP基礎這個階段,我們首先介紹一下什么是AOP,再通過一個快速入門程序,讓大家快速體驗AOP程序的開發。最后再介紹AOP當中所涉及到的一些核心的概念。
2.1 AOP概述
什么是AOP?
- AOP英文全稱:Aspect Oriented Programming(面向切面編程、面向方面編程),其實說白了,面向切面編程就是面向特定方法編程。
那什么又是面向方法編程呢,為什么又需要面向方法編程呢?來我們舉個例子做一個說明:
比如,我們這里有一個項目,項目中開發了很多的業務功能。
然而有一些業務功能執行效率比較低,執行耗時較長,我們需要針對于這些業務方法進行優化。 那首先第一步就需要定位出執行耗時比較長的業務方法,再針對于業務方法再來進行優化。
此時我們就需要統計當前這個項目當中每一個業務方法的執行耗時。那么統計每一個業務方法的執行耗時該怎么實現?
可能多數人首先想到的就是在每一個業務方法運行之前,記錄這個方法運行的開始時間。在這個方法運行完畢之后,再來記錄這個方法運行的結束時間。拿結束時間減去開始時間,不就是這個方法的執行耗時嗎?
以上分析的實現方式是可以解決需求問題的。但是對于一個項目來講,里面會包含很多的業務模塊,每個業務模塊又包含很多增刪改查的方法,如果我們要在每一個模塊下的業務方法中,添加記錄開始時間、結束時間、計算執行耗時的代碼,就會讓程序員的工作變得非常繁瑣。
而AOP面向方法編程,就可以做到在不改動這些原始方法的基礎上,針對特定的方法進行功能的增強。
AOP的作用:在程序運行期間在不修改源代碼的基礎上對已有方法進行增強(無侵入性: 解耦)
我們要想完成統計各個業務方法執行耗時的需求,我們只需要定義一個模板方法,將記錄方法執行耗時這一部分公共的邏輯代碼,定義在模板方法當中,在這個方法開始運行之前,來記錄這個方法運行的開始時間,在方法結束運行的時候,再來記錄方法運行的結束時間,中間就來運行原始的業務方法。
而中間運行的原始業務方法,可能是其中的一個業務方法,比如:我們只想通過 部門管理的 list 方法的執行耗時,那就只有這一個方法是原始業務方法。 而如果,我們是先想統計所有部門管理的業務方法執行耗時,那此時,所有的部門管理的業務方法都是 原始業務方法。 那面向這樣的指定的一個或多個方法進行編程,我們就稱之為 面向切面編程。
那此時,當我們再調用部門管理的 list 業務方法時啊,并不會直接執行 list 方法的邏輯,而是會執行我們所定義的 模板方法 , 然后再模板方法中:
- 記錄方法運行開始時間
- 運行原始的業務方法(那此時原始的業務方法,就是 list 方法)
- 記錄方法運行結束時間,計算方法執行耗時
不論,我們運行的是那個業務方法,最后其實運行的就是我們定義的模板方法,而在模板方法中,就完成了原始方法執行耗時的統計操作 。(那這樣呢,我們就通過一個模板方法就完成了指定的一個或多個業務方法執行耗時的統計)
而大家會發現,這個流程,我們是不是似曾相識啊?
對了,就是和我們之前所學習的動態代理技術是非常類似的。 我們所說的模板方法,其實就是代理對象中所定義的方法,那代理對象中的方法以及根據對應的業務需要, 完成了對應的業務功能,當運行原始業務方法時,就會運行代理對象中的方法,從而實現統計業務方法執行耗時的操作。
其實,AOP面向切面編程和OOP面向對象編程一樣,它們都僅僅是一種編程思想,而動態代理技術是這種思想最主流的實現方式。而Spring的AOP是Spring框架的高級技術,旨在管理bean對象的過程中底層使用動態代理機制,對特定的方法進行編程(功能增強)。
AOP的優勢:
- 減少重復代碼
- 提高開發效率
- 維護方便
2.2 AOP快速入門
在了解了什么是AOP后,我們下面通過一個快速入門程序,體驗下AOP的開發,并掌握Spring中AOP的開發步驟。
**需求:**統計各個業務層方法執行耗時。
實現步驟:
- 導入依賴:在pom.xml中導入AOP的依賴
- 編寫AOP程序:針對于特定方法根據業務需要進行編程
為演示方便,可以自建新項目或導入提供的
springboot-aop-quickstart
項目工程
pom.xml
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
AOP程序:TimeAspect
@Component
@Aspect //當前類為切面類
@Slf4j
public class TimeAspect {@Around("execution(* com.itheima.service.*.*(..))") public Object recordTime(ProceedingJoinPoint pjp) throws Throwable {//記錄方法執行開始時間long begin = System.currentTimeMillis();//執行原始方法Object result = pjp.proceed();//記錄方法執行結束時間long end = System.currentTimeMillis();//計算方法執行耗時log.info(pjp.getSignature()+"執行耗時: {}毫秒",end-begin);return result;}
}
重新啟動SpringBoot服務測試程序:
- 查詢3號部門信息
我們可以再測試下:查詢所有部門信息(同樣執行AOP程序)
我們通過AOP入門程序完成了業務方法執行耗時的統計,那其實AOP的功能遠不止于此,常見的應用場景如下:
- 記錄系統的操作日志
- 權限控制
- 事務管理:我們前面所講解的Spring事務管理,底層其實也是通過AOP來實現的,只要添加@Transactional注解之后,AOP程序自動會在原始方法運行前先來開啟事務,在原始方法運行完畢之后提交或回滾事務
這些都是AOP應用的典型場景。
通過入門程序,我們也應該感受到了AOP面向切面編程的一些優勢:
-
代碼無侵入:沒有修改原始的業務方法,就已經對原始的業務方法進行了功能的增強或者是功能的改變
-
減少了重復代碼
-
提高開發效率
-
維護方便
2.3 AOP核心概念
通過SpringAOP的快速入門,感受了一下AOP面向切面編程的開發方式。下面我們再來學習AOP當中涉及到的一些核心概念。
1. 連接點:JoinPoint,可以被AOP控制的方法(暗含方法執行時的相關信息)
? 連接點指的是可以被aop控制的方法。例如:入門程序當中所有的業務方法都是可以被aop控制的方法。
?
? 在SpringAOP提供的JoinPoint當中,封裝了連接點方法在執行時的相關信息。(后面會有具體的講解)
2. 通知:Advice,指哪些重復的邏輯,也就是共性功能(最終體現為一個方法)
? 在入門程序中是需要統計各個業務方法的執行耗時的,此時我們就需要在這些業務方法運行開始之前,先記錄這個方法運行的開始時間,在每一個業務方法運行結束的時候,再來記錄這個方法運行的結束時間。
? 但是在AOP面向切面編程當中,我們只需要將這部分重復的代碼邏輯抽取出來單獨定義。抽取出來的這一部分重復的邏輯,也就是共性的功能。
3. 切入點:PointCut,匹配連接點的條件,通知僅會在切入點方法執行時被應用
? 在通知當中,我們所定義的共性功能到底要應用在哪些方法上?此時就涉及到了切入點pointcut概念。切入點指的是匹配連接點的條件。通知僅會在切入點方法運行時才會被應用。
? 在aop的開發當中,我們通常會通過一個切入點表達式來描述切入點(后面會有詳解)。
?
? 假如:切入點表達式改為DeptServiceImpl.list(),此時就代表僅僅只有list這一個方法是切入點。只有list()方法在運行的時候才會應用通知。
?
4. 切面:Aspect,描述通知與切入點的對應關系(通知+切入點)
? 當通知和切入點結合在一起,就形成了一個切面。通過切面就能夠描述當前aop程序需要針對于哪個原始方法,在什么時候執行什么樣的操作。
? 切面所在的類,我們一般稱為切面類(被@Aspect注解標識的類)
?
5. 目標對象:Target,通知所應用的對象
? 目標對象指的就是通知所應用的對象,我們就稱之為目標對象。
?
AOP的核心概念我們介紹完畢之后,接下來我們再來分析一下我們所定義的通知是如何與目標對象結合在一起,對目標對象當中的方法進行功能增強的。
Spring的AOP底層是基于動態代理技術來實現的,也就是說在程序運行的時候,會自動的基于動態代理技術為目標對象生成一個對應的代理對象。在代理對象當中就會對目標對象當中的原始方法進行功能的增強。