Spring AOP詳解面向切面編程思想
1. 什么是 Spring AOP
AOP (Aspect Oriented Programming):
面向切面編程, 它是一種思想, 它是對(duì)某一類(lèi)事情的集中處理.
例如, 在沒(méi)有學(xué)習(xí)AOP之前, 之前的判斷當(dāng)前登錄狀態(tài), 就需要在每一個(gè)頁(yè)面都實(shí)現(xiàn)登錄校驗(yàn), 在有了AOP之后, 外面只需在某一處配置以下, 所有的頁(yè)面就都可以實(shí)現(xiàn)登錄驗(yàn)證了, 就不需要寫(xiě)太多重復(fù)的代碼,
Spring AOP
, 是一個(gè)框架, 提高了一種對(duì) AOP 思想的實(shí)現(xiàn).
2. AOP 的組成
2.1 切面 (Aspect)
切面由切點(diǎn)和通知組成, 它既包含了橫切邏輯的定義, 也包括了連接點(diǎn)的定義.
切面是包含了: 通知, 切點(diǎn)和切面的類(lèi), 相當(dāng)于 AOP 實(shí)現(xiàn)的某個(gè)功能的集合
2.2 切點(diǎn) (Pointcur)
切點(diǎn)的作用就是提供一組規(guī)則 (使用 AspectJ pointcut expression language 來(lái)描述) 來(lái)匹配 連接點(diǎn), 給滿(mǎn)足規(guī)則的 連接點(diǎn)添加 Advice
切點(diǎn)相當(dāng)于保存了眾多連接點(diǎn)的一個(gè)集合
2.3 連接點(diǎn) (Join Point)
應(yīng)用執(zhí)行過(guò)程中能夠插入切面的一個(gè)點(diǎn), 這個(gè)點(diǎn)可以是方法的調(diào)用時(shí), 拋出異常時(shí), 甚至修改字段時(shí). 切面代碼可以利用這些點(diǎn)插入到應(yīng)用的正常流程之中, 并添加新的行為.
連接點(diǎn)相當(dāng)于需要被增強(qiáng)的某個(gè) AOP 功能的所有方法.
2.4 通知 (Advice)
定義了切面是什么, 何時(shí)使用, 其描述切面要完成的工作, 還解決何時(shí)執(zhí)行這個(gè)工作的問(wèn)題,
Spring切面類(lèi)中, 可以在方法上使用以下注解, 會(huì)設(shè)置方法為通知方法, 在滿(mǎn)足條件后會(huì)通知本方法進(jìn)行調(diào)用.
?法進(jìn)?調(diào)用:
前置通知使用 @Before
:通知方法會(huì)在目標(biāo)方法調(diào)用之前執(zhí)行.
后置通知使用 @After
:通知方法會(huì)在目標(biāo)方法返回或者拋出異常后調(diào)用.
返回之后通知使用 @AfterReturning
:通知方法會(huì)在目標(biāo)方法返回后調(diào)用.
拋異常后通知使用 @AfterThrowing
:通知方法會(huì)在目標(biāo)方法拋出異常后調(diào)用.
環(huán)繞通知使用 @Around
:通知包裹了被通知的方法, 在被通知的方法通知之前和調(diào)用之后執(zhí)行自定義的行為.
3. Spring AOP 的使用
3.1 添加 AOP 框架
在 pom.xml 中添加依賴(lài)
<!-- https://mvnrepository.com/artifact/org.springframework.boot/springboot-starter-aop --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
3.2 定義切面和切點(diǎn)
@Aspect // 定義切面 @Component public class UserAspect { // 切點(diǎn) (配置攔截規(guī)則) @Pointcut("execution(* com.example.demo.controller.UserController.*)") public void pointcut() { // 這是一個(gè)空方法, 不需要有具體的實(shí)現(xiàn) } }
切點(diǎn)表達(dá)式注意事項(xiàng)
AspectJ 支持三種通配符
*
: 匹配任意字符, 只匹配一個(gè)元素 (包, 類(lèi), 方法, 方法參數(shù))..
: 匹配任意字符,可以匹配多個(gè)元素, 在標(biāo)識(shí)類(lèi)時(shí), 必須聯(lián)合*
使用+
: 表示按類(lèi)型匹配指定類(lèi)和所有類(lèi), 必須跟在類(lèi)名后面, 如com.cad.Car+
, 表示繼承該類(lèi)的所有子類(lèi)包括本身
execution()
是最常用的切點(diǎn)函數(shù)
語(yǔ)法為: execution(<修飾符> <返回類(lèi)型> <包.類(lèi).方法(參數(shù))> <異常>)
(注意: 修飾符和異??梢允÷?
示例:
execution(* com.cad.demo.User.*(..))
:匹配 User 類(lèi)?的所有?法。execution(* com.cad.demo.User+.*(..))
:匹配該類(lèi)的?類(lèi)包括該類(lèi)的所有?法。execution(* com.cad.*.*(..))
:匹配 com.cad 包下的所有類(lèi)的所有?法。execution(* com.cad..*.*(..))
:匹配 com.cad 包下、?孫包下所有類(lèi)的所有?法。execution(* addUser(String, int))
:匹配 addUser ?法,且第?個(gè)參數(shù)類(lèi)型是 String,第?個(gè)參數(shù)類(lèi)型是 int。
3.3 定義通知 (五種)
@Aspect // 定義切面 @Component public class UserAspect { // 切點(diǎn) (配置攔截規(guī)則) @Pointcut("execution(* com.example.demo.controller.UserController.*(..))") public void pointcut() { // 這是一個(gè)空方法, 不需要有具體的實(shí)現(xiàn) } @Before("pointcut()") public void doBefore(){ System.out.println("執(zhí)行 Before 方法"); } @After("pointcut()") public void doAfter(){ System.out.println("執(zhí)行 After 方法"); } @AfterReturning("pointcut()") public void doAfterReturning() { System.out.println("執(zhí)行 AfterReturning 方法"); } @AfterThrowing("pointcut()") public void doAfterThrowing() { System.out.println("執(zhí)行 AfterThrowing 方法"); } @Around("pointcut()") public Object doAround(ProceedingJoinPoint joinPoint) { Object object = null; System.out.println("Around 方法開(kāi)始執(zhí)行"); try { // 執(zhí)行攔截方法 object = joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } System.out.println("Around 方法結(jié)束執(zhí)行"); return object; } }
正常時(shí)
拋出異常時(shí)
4. Spring AOP 實(shí)現(xiàn)原理
Spring AOP 是構(gòu)建在動(dòng)態(tài)代理基礎(chǔ)上, 因此 Spring 對(duì) AOP 的支持局限于方法級(jí)別的攔截.
Spring AOP 是基于動(dòng)態(tài)代理實(shí)現(xiàn)的.
動(dòng)態(tài)代理分為兩類(lèi):
- JDK Proxy(JDK 動(dòng)態(tài)代理機(jī)制)
- CGLIB 動(dòng)態(tài)代理
默認(rèn)情況下, 實(shí)現(xiàn)了接口的類(lèi), 使用 AOP 會(huì)基于 JDK 生成代理類(lèi), 沒(méi)有實(shí)現(xiàn)接口的類(lèi), 會(huì)基于 CGLIB 生成代理類(lèi)
4.1 織入 (Weaving)
代理的生成時(shí)機(jī)
織?是把切面應(yīng)用到目標(biāo)對(duì)象并創(chuàng)建新的代理對(duì)象的過(guò)程,切面在指定的連接點(diǎn)被織?到目標(biāo)對(duì)象中。
在目標(biāo)對(duì)象的?命周期里有多個(gè)點(diǎn)可以進(jìn)?織入:
- 編譯期:切面在目標(biāo)類(lèi)編譯時(shí)被織?。這種?式需要特殊的編譯器。AspectJ的織?編譯器就是以這種方式織入切?的。
- 類(lèi)加載器:切?在目標(biāo)類(lèi)加載到JVM時(shí)被織入。這種方式需要特殊的類(lèi)加載器(ClassLoader),它可以在目標(biāo)類(lèi)被引入應(yīng)用之前增強(qiáng)該目標(biāo)類(lèi)的字節(jié)碼。AspectJ5的加載時(shí)織入(load-time weaving. LTW)就支持以這種方式織入切面。
- 運(yùn)行期:切面在應(yīng)用運(yùn)行的某?時(shí)刻被織入。?般情況下,在織入切面時(shí),AOP容器會(huì)為目標(biāo)對(duì)象動(dòng)態(tài)創(chuàng)建一個(gè)代理對(duì)象. Spring AOP 就是以這種方式織入切面的
4.2 JDK 和 CGLIB 實(shí)現(xiàn)的區(qū)別
- JDK 實(shí)現(xiàn)要求被代理類(lèi)必須實(shí)現(xiàn)接口, 之后是通過(guò)
InvocationHandler
及Proxy
, 在運(yùn)行時(shí)動(dòng)態(tài)的在內(nèi)存中生成了代理類(lèi)對(duì)象, 該代理對(duì)象是通過(guò)實(shí)現(xiàn)同樣的接口實(shí)現(xiàn) (類(lèi)似靜態(tài)代理接口實(shí)現(xiàn)的方式), 只是該代理類(lèi)是在運(yùn)行期時(shí),動(dòng)態(tài)的織入統(tǒng)一的業(yè)務(wù)邏輯字節(jié)碼來(lái)完成. - CGLIB 實(shí)現(xiàn), 被代理類(lèi)可以不實(shí)現(xiàn)接口, 是通過(guò)繼承被代理類(lèi), 在運(yùn)行時(shí)動(dòng)態(tài)的生成代理類(lèi)對(duì)象.
到此這篇關(guān)于Spring AOP詳解面向切面編程思想的文章就介紹到這了,更多相關(guān)Spring AOP切面編程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)獲取某年某月第一天/最后一天的方法
這篇文章主要介紹了Java實(shí)現(xiàn)獲取某年某月第一天/最后一天的方法,涉及java日期運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下2018-02-02詳解Java中static關(guān)鍵字和內(nèi)部類(lèi)的使用
這篇文章主要為大家詳細(xì)介紹了Java中static關(guān)鍵字和內(nèi)部類(lèi)的使用,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-08-08Java中SynchronousQueue的底層實(shí)現(xiàn)原理剖析
BlockingQueue的實(shí)現(xiàn)類(lèi)中,有一種阻塞隊(duì)列比較特殊,就是SynchronousQueue(同步移交隊(duì)列),隊(duì)列長(zhǎng)度為0。本文就來(lái)剖析一下SynchronousQueue的底層實(shí)現(xiàn)原理,感興趣的可以了解一下2022-11-11詳解SpringBoot如何優(yōu)雅的進(jìn)行全局異常處理
在SpringBoot的開(kāi)發(fā)中,為了提高程序運(yùn)行的魯棒性,我們經(jīng)常需要對(duì)各種程序異常進(jìn)行處理,但是如果在每個(gè)出異常的地方進(jìn)行單獨(dú)處理的話(huà),這會(huì)引入大量業(yè)務(wù)不相關(guān)的異常處理代碼,這篇文章帶大家了解一下如何優(yōu)雅的進(jìn)行全局異常處理2023-07-07理解 MyBatis 是如何在 Spring 容器中初始化的
這篇文章主要介紹了理解 MyBatis 是如何在 Spring 容器中初始化的,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11關(guān)于Java中使用jdbc連接數(shù)據(jù)庫(kù)中文出現(xiàn)亂碼的問(wèn)題
這篇文章主要介紹了關(guān)于Java中使用jdbc連接數(shù)據(jù)庫(kù)中文出現(xiàn)亂碼的問(wèn)題,默認(rèn)的編碼和數(shù)據(jù)庫(kù)表中的數(shù)據(jù)使用的編碼是不一致的,如果是中文,那么在數(shù)據(jù)庫(kù)中執(zhí)行時(shí)已經(jīng)是亂碼了,需要的朋友可以參考下2023-04-04