Spring Aop 常見注解和執(zhí)行順序
來源:juejin.cn/post/7062506923194581029
Spring 一開始最強(qiáng)大的就是 IOC / AOP 兩大核心功能,我們今天一起來學(xué)習(xí)一下 Spring AOP 常見注解和執(zhí)行順序。Spring Aop 的常用注解
首先我們一起來回顧一下 Spring Aop 中常用的幾個(gè)注解:
@Before前置通知:目標(biāo)方法之前執(zhí)行@After后置通知:目標(biāo)方法之后執(zhí)行(始終執(zhí)行)@AfterReturning返回之后通知:執(zhí)行方法結(jié)束之前執(zhí)行(異常不執(zhí)行)@AfterThrowing異常通知:出香異常后執(zhí)行@Around環(huán)繞通知:環(huán)繞目標(biāo)方法執(zhí)行
常見問題
1、你肯定知道 Spring ?, 那說說 Aop 的去全部通知順序, Spring Boot 或者 Spring Boot 2 對(duì) aop 的執(zhí)行順序影響?
2、說說你在 AOP 中遇到的那些坑?
示例代碼
下面我們先快速構(gòu)建一個(gè) spring aop 的 demo 程序來一起討論 spring aop 中的一些細(xì)節(jié)。
配置文件
為了方便我直接使用 spring-boot 進(jìn)行快速的項(xiàng)目搭建,大家可以使用 idea 的spring-boot 項(xiàng)目快速創(chuàng)建功能,或者去 start.spring.io 上面去快速創(chuàng)建spring-boot 應(yīng)用。
因?yàn)楸救私?jīng)常手動(dòng)去網(wǎng)上貼一些依賴導(dǎo)致,依賴沖突服務(wù)啟動(dòng)失敗等一些問題。
plugins?{
????id?'org.springframework.boot'?version?'2.6.3'
????id?'io.spring.dependency-management'?version?'1.0.11.RELEASE'
????id?'java'
}
group?'io.zhengsh'
version?'1.0-SNAPSHOT'
repositories?{
????mavenCentral()
????maven?{?url?'https://repo.spring.io/milestone'?}
????maven?{?url?'https://repo.spring.io/snapshot'?}
}
dependencies?{
????#?其實(shí)這里也可以不增加?web?配置,為了試驗(yàn)簡單,大家請(qǐng)忽略?
????implementation?'org.springframework.boot:spring-boot-starter-web'
????implementation?'org.springframework.boot:spring-boot-starter-actuator'
????implementation?'org.springframework.boot:spring-boot-starter-aop'
????
????testImplementation?'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test')?{
????useJUnitPlatform()
}
接口類
首先我們需要定義一個(gè)接口。我們這里可以再來回顧一下 JDK 的默認(rèn)代理實(shí)現(xiàn)的選擇:
如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口,則默認(rèn)采用JDK動(dòng)態(tài)代理 如果目標(biāo)對(duì)象沒有實(shí)現(xiàn)接口,則采用進(jìn)行動(dòng)態(tài)代理 如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口,且強(qiáng)制Cglib,則使用cglib代理
這塊的邏輯在 DefaultAopProxyFactory 大家有興趣可以去看看。
public?interface?CalcService?{
????public?int?div(int?x,?int?y);
}
實(shí)現(xiàn)類
這里我門就簡單一點(diǎn)做一個(gè)除法操作,可以模擬正常也可以很容易的模擬錯(cuò)誤。
@Service
public?class?CalcServiceImpl?implements?CalcService?{
????@Override
????public?int?div(int?x,?int?y)?{
????????int?result?=?x?/?y;
????????System.out.println("====> CalcServiceImpl 被調(diào)用了,我們的計(jì)算結(jié)果是:"?+?result);
????????return?result;
????}
}
aop 攔截器
申明一個(gè)攔截器我們要為當(dāng)前對(duì)象增加 @Aspect 和 @Component ,筆者之前也是才踩過這樣的坑,只加了一個(gè)。
其實(shí)這塊我剛開始也不是很理解,但是我看了 Aspect 注解的定義我就清楚了

這里面根本就沒有 Bean 的定義。所以我們還是乖乖的加上兩個(gè)注解。
還有就是如果當(dāng)測試的時(shí)候需要開啟Aop 的支持為配置類上增加@EnableAspectJAutoProxy 注解。
其實(shí) Aop 使用就三個(gè)步驟:
定義 Aspect 定義切面 定義 Pointcut 就是定義我們切入點(diǎn) 定義具體的通知,比如: @After, @Before 等。
@Aspect
@Component
public?class?MyAspect?{
????@Pointcut("execution(*?io.zhengsh.spring.service.impl..*.*(..))")
????public?void?divPointCut()?{
????}
????@Before("divPointCut()")
????public?void?beforeNotify()?{
????????System.out.println("----===>>?@Before?我是前置通知");
????}
????@After("divPointCut")
????public?void?afterNotify()?{
????????System.out.println("----===>>?@After??我是后置通知");
????}
????@AfterReturning("divPointCut")
????public?void?afterReturningNotify()?{
????????System.out.println("----===>>?@AfterReturning?我是前置通知");
????}
????@AfterThrowing("divPointCut")
????public?void?afterThrowingNotify()?{
????????System.out.println("----===>>?@AfterThrowing?我是異常通知");
????}
????@Around("divPointCut")
????public?Object?around(ProceedingJoinPoint?proceedingJoinPoint)?throws?Throwable?{
????????Object?retVal;
????????System.out.println("----===>>?@Around?環(huán)繞通知之前?AAA");
????????retVal?=?proceedingJoinPoint.proceed();
????????System.out.println("----===>>?@Around?環(huán)繞通知之后?BBB");
????????return?retVal;
????}
}
測試類
其實(shí)我這個(gè)測試類,雖然用了 @Test 注解,但是我這個(gè)類更加像一個(gè) main 方法把:如下所示:

執(zhí)行結(jié)論
結(jié)果記錄:spring 4.x, spring-boot 1.5.9
無法現(xiàn)在依賴,所以無法試驗(yàn)
我直接說一下結(jié)論:Spring 4 中環(huán)繞通知是在最里面執(zhí)行的
結(jié)果記錄:spring 版本5.3.15 springboot 版本2.6.3

多切面的情況
多個(gè)切面的情況下,可以通過@Order指定先后順序,數(shù)字越小,優(yōu)先級(jí)越高。如下圖所示:

代理失效場景
下面一種場景會(huì)導(dǎo)致 aop 代理失效,因?yàn)槲覀冊(cè)趫?zhí)行 a 方法的時(shí)候其實(shí)本質(zhì)是執(zhí)行 AServer#a 的方法攔截器(MethodInterceptor)鏈, 當(dāng)我們?cè)?a 方法內(nèi)直接執(zhí)行b(), 其實(shí)本質(zhì)就相當(dāng)于 this.b() , 這個(gè)時(shí)候由執(zhí)行 a方法是調(diào)用到 a 的原始對(duì)象相當(dāng)于是 this 調(diào)用,那么會(huì)導(dǎo)致 b() 方法的代理失效。這個(gè)問題也是我們開發(fā)者在開發(fā)過程中最常遇到的一個(gè)問題。
@Service
public?class?AService?{
????
????public?void?a()?{
????????System.out.println("......?a");
????????b();
????}
????
????public?void?b()?{
????????System.out.println("......?b");
????}
}
END
推薦閱讀 一鍵生成Springboot & Vue項(xiàng)目!【畢設(shè)神器】
Java可視化編程工具系列(一)
Java可視化編程工具系列(二)
順便給大家推薦一個(gè)GitHub項(xiàng)目,這個(gè) GitHub 整理了上千本常用技術(shù)PDF,絕大部分核心的技術(shù)書籍都可以在這里找到,
GitHub地址:https://github.com/javadevbooks/books
電子書已經(jīng)更新好了,你們需要的可以自行下載了,記得點(diǎn)一個(gè)star,持續(xù)更新中..

