Spring?Aop常見(jiàn)注解與執(zhí)行順序詳解
Spring 一開(kāi)始最強(qiáng)大的就是 IOC / AOP 兩大核心功能,我們今天一起來(lái)學(xué)習(xí)一下 Spring AOP 常見(jiàn)注解和執(zhí)行順序。
Spring Aop 的常用注解
首先我們一起來(lái)回顧一下 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í)行
常見(jiàn)問(wèn)題
1、你肯定知道 Spring , 那說(shuō)說(shuō) Aop 的去全部通知順序, Spring Boot 或者 Spring Boot 2 對(duì) aop 的執(zhí)行順序影響?
2、說(shuō)說(shuō)你在 AOP 中遇到的那些坑?
示例代碼
下面我們先快速構(gòu)建一個(gè) spring aop 的 demo 程序來(lái)一起討論 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)失敗等一些問(wèn)題)。
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)簡(jiǎ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è)接口。我們這里可以再來(lái)回顧一下 JDK 的默認(rèn)代理實(shí)現(xiàn)的選擇:
如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口,則默認(rèn)采用JDK動(dòng)態(tài)代理
如果目標(biāo)對(duì)象沒(méi)有實(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)類
這里我門(mén)就簡(jiǎ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 ,筆者之前也是才踩過(guò)這樣的坑,只加了一個(gè)。
其實(shí)這塊我剛開(kāi)始也不是很理解,但是我看了 Aspect 注解的定義我就清楚了
這里面根本就沒(méi)有 Bean 的定義。所以我們還是乖乖的加上兩個(gè)注解。 還有就是如果當(dāng)測(cè)試的時(shí)候需要開(kāi)啟Aop 的支持為配置類上增加 @EnableAspectJAutoProxy
注解。
其實(shí) Aop 使用就三個(gè)步驟:
1、定義 Aspect 定義切面
2、定義 Pointcut 就是定義我們切入點(diǎn)
3、定義具體的通知,比如: @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; } }
測(cè)試類
其實(shí)我這個(gè)測(cè)試類,雖然用了 @Test 注解,但是我這個(gè)類更加像一個(gè) main 方法把:如下所示:
執(zhí)行結(jié)論
結(jié)果記錄:spring 4.x, spring-boot 1.5.9
無(wú)法現(xiàn)在依賴,所以無(wú)法試驗(yàn)
我直接說(shuō)一下結(jié)論: Spring 4 中環(huán)繞通知是在最里面執(zhí)行的
結(jié)果記錄:spring 版本5.3.15 springboot 版本2.6.3
多切面的情況
多個(gè)切面的情況下,可以通過(guò)@Order指定先后順序,數(shù)字越小,優(yōu)先級(jí)越高。 如下圖所示:
代理失效場(chǎng)景
下面一種場(chǎng)景會(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è)問(wèn)題也是我們開(kāi)發(fā)者在開(kāi)發(fā)過(guò)程中最常遇到的一個(gè)問(wèn)題。
@Service public class AService { public void a() { System.out.println("...... a"); b(); } public void b() { System.out.println("...... b"); } }
總結(jié)
到此這篇關(guān)于Spring Aop常見(jiàn)注解與執(zhí)行順序的文章就介紹到這了,更多相關(guān)Spring Aop常見(jiàn)注解內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java接口私有方法實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了java接口私有方法實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11java使用JDBC動(dòng)態(tài)創(chuàng)建數(shù)據(jù)表及SQL預(yù)處理的方法
這篇文章主要介紹了java使用JDBC動(dòng)態(tài)創(chuàng)建數(shù)據(jù)表及SQL預(yù)處理的方法,涉及JDBC操作數(shù)據(jù)庫(kù)的連接、創(chuàng)建表、添加數(shù)據(jù)、查詢等相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-08-08詳解Mybatis是如何把數(shù)據(jù)庫(kù)數(shù)據(jù)封裝到對(duì)象中的
這篇文章主要介紹了Mybatis是如何把數(shù)據(jù)庫(kù)數(shù)據(jù)封裝到對(duì)象中的,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12IDEA插件Statistic統(tǒng)計(jì)代碼快速分辨爛項(xiàng)目
這篇文章主要為大家介紹了使用IDEA插件Statistic來(lái)統(tǒng)計(jì)項(xiàng)目代碼,幫助大家快速識(shí)別出爛項(xiàng)目,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-01-01詳解Spring 兩種注入的方式(Set和構(gòu)造)實(shí)例
本篇文章主要介紹了Spring 兩種注入的方式(Set和構(gòu)造)實(shí)例,Spring框架主要提供了Set注入和構(gòu)造注入兩種依賴注入方式。有興趣的可以了解一下。2017-02-02Java線程池的幾種實(shí)現(xiàn)方法及常見(jiàn)問(wèn)題解答
下面小編就為大家?guī)?lái)一篇Java線程池的幾種實(shí)現(xiàn)方法及常見(jiàn)問(wèn)題解答。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-05-05Java和C語(yǔ)言分別實(shí)現(xiàn)水仙花數(shù)及拓展代碼
這篇文章主要介紹了分別用Java和C語(yǔ)言實(shí)現(xiàn)水仙花數(shù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-11-11MyBatis-Plus 修改和添加自動(dòng)填充時(shí)間方式
這篇文章主要介紹了MyBatis-Plus 修改和添加自動(dòng)填充時(shí)間方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08