Spring?AOP?的實(shí)現(xiàn)和切點(diǎn)表達(dá)式的實(shí)現(xiàn)方式
1. 快速入手
AOP:就是面相切面編程,切面指的就是某一類特定的問(wèn)題,也可以理解為面相特定方法編程,例如之前使用的攔截器,就是 AOP 思想的一種應(yīng)用,統(tǒng)一數(shù)據(jù)返回格式和統(tǒng)一異常處理也是 AOP 思想的實(shí)現(xiàn)方式
比如說(shuō)需要統(tǒng)計(jì)每個(gè)方法執(zhí)行的耗時(shí),如果正常來(lái)寫的話,需要在方法的開頭和結(jié)尾來(lái)定義時(shí)間戳相減

如果有很多方法都需要計(jì)算的話,總不能每個(gè)方法都寫這些重復(fù)的代碼吧,接下來(lái)看通過(guò)使用 AOP 思想是如何實(shí)現(xiàn)的
首先需要添加對(duì)應(yīng)的依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
然后需要添加@Component注解,

@Slf4j
@Aspect
@Component
public class TimeRecordAspect {
@Around("execution(* com.example.springbook.controller.*.*(..))")
public Object timeRecord(ProceedingJoinPoint pjt){
//記錄開始時(shí)間
long start = System.currentTimeMillis();
//執(zhí)行目標(biāo)方法
Object o = null;
try {
o = pjt.proceed();
} catch (Throwable e){
throw new RuntimeException(e);
}
log.info(pjt.getSignature() + "執(zhí)行耗時(shí):" + (System.currentTimeMillis() - start));
return o;
}
}再去調(diào)用接口的話就會(huì)計(jì)算出來(lái)對(duì)應(yīng)方法消耗的時(shí)間

來(lái)簡(jiǎn)單分析一下上面的代碼:

2. 通知類型
Spring AOP 的通知類型有以下幾種
?@Around:環(huán)繞通知,在目標(biāo)方法前、后都被執(zhí)行。
?@Before:前置通知,在目標(biāo)方法前被執(zhí)行。
?@After:后置通知,在目標(biāo)方法后被執(zhí)行,無(wú)論是否有異常都會(huì)執(zhí)行。
?@AfterReturning:返回后通知,在目標(biāo)方法后被執(zhí)行,有異常不會(huì)執(zhí)行。
?@AfterThrowing:異常后通知,發(fā)生異常后執(zhí)行。
接下來(lái)同時(shí)測(cè)試一下這些通知類型,

來(lái)看一下接口正常返回的情況下的執(zhí)行順序

再來(lái)看接口發(fā)生異常的情況下的執(zhí)行順序:

從上面的結(jié)果上就可以看出,Around 可以完成其他類型的功能
需要注意的是:
- @Around 環(huán)繞通知需要調(diào)用 ProceedingJoinPoint.proceed () 來(lái)讓原始方法執(zhí)行,其他通知不需要考慮目標(biāo)方法執(zhí)行。
- @Around 環(huán)繞通知方法的返回值,必須指定為 Object,來(lái)接收原始方法的返回值,否則原始方法執(zhí)行完畢,是獲取不到返回值的。

3. @Pointcut
在上面的代碼中還存在一個(gè)問(wèn)題,每次寫一個(gè)方法都需要寫一個(gè)切點(diǎn)表達(dá)式,如果說(shuō)更換切點(diǎn)的話,那么所有的切點(diǎn)表達(dá)式都要修改一下,就可以通過(guò)@Pointcut 注解,把公共的切點(diǎn)表達(dá)式提取出來(lái),需要用到時(shí)引用該切點(diǎn)表達(dá)式即可

這樣提取出來(lái),其他方法想要調(diào)用直接寫上方法名稱即可,和定義的常量類似,那么同一個(gè)類下可以直接調(diào)用,如果是不同的類的話需要把全限定名寫上,并寫明是 xx 類的 xx 方法
@Around("com.example.springaop.aspect.AspectDemo.pt()")執(zhí)行之后也是生效了

但是如果定義時(shí)設(shè)置為了 private 的話其他類就不能執(zhí)行了
@Pointcut("execution(* com.example.springaop.controller.*.*(..))")
private void pt(){
}4. 切面優(yōu)先級(jí)
當(dāng)在一個(gè)項(xiàng)目中定義了多個(gè)切面類時(shí),并且這些切面類的多個(gè)切入點(diǎn)都匹配到了同一個(gè)目標(biāo)方法,那么目標(biāo)方法執(zhí)行的時(shí)候,這些切面類中的通知方法都會(huì)執(zhí)行,那么這時(shí)就會(huì)有一個(gè)優(yōu)先級(jí),哪個(gè)切面類先執(zhí)行

通過(guò)測(cè)試發(fā)現(xiàn),執(zhí)行的順序也是類似于一個(gè)切面的

關(guān)于切面類的執(zhí)行順序,默認(rèn)是按照類名的字典序來(lái)執(zhí)行的

可以@Order注解通過(guò)來(lái)修改優(yōu)先級(jí)

這樣 AspectDemo2 的優(yōu)先級(jí)就變?yōu)樽罡叩牧耍拖葓?zhí)行,也就是數(shù)字越大優(yōu)先級(jí)越高
5. 切點(diǎn)表達(dá)式
5.1. execution 表達(dá)式

訪問(wèn)修飾符和異常可以省略
- * 表示通配符,匹配任意字符,不過(guò)只能匹配一個(gè)元素(即只能匹配任意一種返回類型,包名,類名,方法或者方法參數(shù)),一層包使用一個(gè) *
- ' . . ' 表示匹配多個(gè)連續(xù)的任意符號(hào),可以通配任意層級(jí)的包,或者任意類型,任意個(gè)數(shù)的參數(shù),使用 .. 配置包名,表示此包以及此包下的所有子包
來(lái)看具體示例:

表示匹配 TestController 下的 public 修飾,返回類型為 String 方法名為 t1,無(wú)參方法

如果省略訪問(wèn)修飾符,表示匹配 public 修飾或者 protected 修飾的方法

表示匹配所有返回類型

如果再把方法名設(shè)為 * 表示所有方法,上面就是匹配該類下的所有無(wú)參方法

如果設(shè)為 .. 就表示所有方法,無(wú)論有參還是無(wú)參都能匹配

表示 controller 包下的所有類的所有方法都匹配

表示 com 下類名為 TestController 的所有方法

表示 demo 下的所有包的所有類的所有方法 5.2. @annotation
使用 execution 表達(dá)式匹配的方法都是具有一定規(guī)律的,比如 xx 包的 xx 類的 xx 方法,那么如果沒(méi)有規(guī)律可循的話就需要使用 @annotation 注解了
首先,可以通過(guò)自定義注解的方式,自定義注解的創(chuàng)建需要選擇 @Annotation

@Retention(RetentionPolicy.RUNTIME) //注解的有效階段
@Target({ElementType.METHOD}) //表示方法注解
public @interface TimeRecord {
}然后在原來(lái)計(jì)時(shí)的方法上來(lái)使用 @annotation 來(lái)指明要使用的注解

接下來(lái)只要是添加了自定義的注解都會(huì)執(zhí)行這里的方法

通過(guò)這種方式就實(shí)現(xiàn)了想要給哪個(gè)方法生效就直接加上注解就可以了
除了自定義注解,其他現(xiàn)存的注解也是可以這樣使用的

例如,可以把@RequestMapping的路徑寫在@annotation里,就表示只要加了@RequestMapping的方法都可以生效

到此這篇關(guān)于Spring AOP 的實(shí)現(xiàn)和切點(diǎn)表達(dá)式的實(shí)現(xiàn)方式的文章就介紹到這了,更多相關(guān)Spring AOP 切點(diǎn)表達(dá)式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于Spring p標(biāo)簽和c標(biāo)簽注入方式
這篇文章主要介紹了Spring p標(biāo)簽和c標(biāo)簽注入方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
Springboot @Value獲取值為空問(wèn)題解決方案
這篇文章主要介紹了Springboot @Value獲取值為空問(wèn)題解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02
Java中notify和notifyAll的區(qū)別及何時(shí)使用
本文主要介紹了Java中notify和notifyAll的區(qū)別及何時(shí)使用,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
Struts2實(shí)現(xiàn)文件上傳功能實(shí)例解析
這篇文章主要介紹了Struts2實(shí)現(xiàn)文件上傳功能實(shí)例解析,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-01-01
mybatis中映射文件include標(biāo)簽的應(yīng)用
這篇文章主要介紹了mybatis中映射文件include標(biāo)簽的應(yīng)用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11
Java 基礎(chǔ)之內(nèi)部類詳解及實(shí)例
這篇文章主要介紹了 Java 基礎(chǔ)之內(nèi)部類詳解及實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-03-03
在Mac OS上安裝Java以及配置環(huán)境變量的基本方法
這篇文章主要介紹了在Mac OS上安裝Java以及配置環(huán)境變量的基本方法,包括查看所安裝Java版本的方法,需要的朋友可以參考下2015-10-10

