Spring AOP之@Around,@AfterReturning使用、切不進(jìn)去的解決方案
本文主要舉幾個(gè)工作中典型AOP的實(shí)操案例,還有經(jīng)常出現(xiàn)的問(wèn)題(切不進(jìn)去,ctrl+左鍵跳不到被切方法中)等等。
本文對(duì)于AOP的實(shí)現(xiàn)原理概不討論,百度一搜有的是。
AOP的使用背景和好處
比如A模塊是公司的核心模塊,這塊代碼未經(jīng)允許不得輕易篡改。
但是你又有新的需求,需要在公司的核心模塊的某個(gè)方法上進(jìn)行增強(qiáng)(比如在執(zhí)行核心方法的之前打印自定義日志,或者修改該核心方法的入?yún)⒑头祷刂档鹊龋?/p>
這樣你就可以在不修改核心模塊源碼的情況下,對(duì)源代碼的方法進(jìn)行增強(qiáng),擴(kuò)展原來(lái)方法的一些功能。
這樣既能保證源代碼不被破壞,又可以擴(kuò)展源代碼現(xiàn)有的功能。
一、幾種使用姿勢(shì)
1、@AfterReturning和@Before
@AfterReturning是后置方法,在目標(biāo)方法執(zhí)行后執(zhí)行,@Before是前置方法,在目標(biāo)方法執(zhí)行前執(zhí)行
它們一般配合JoinPoint來(lái)使用(不能配合ProceedingJoinPoint,會(huì)報(bào)錯(cuò))。
直接看例子:
被切的方法:
@Service public class OriFuncImpl implements OriFunc{ @Override public String ori(String str){ System.out.println("執(zhí)行了原方法"); return str; } }
使用@AfterReturning 和 @Before:
@Aspect @Component public class AopFunc { @Pointcut("execution(* com.daji.aop_test.OriFuncImpl.ori(..))") public void test() { } @Before("test()") public void before(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); //獲取方法入?yún)? System.out.println("原方法的入?yún)⑹牵?+args[0]); System.out.println("原方法執(zhí)行前會(huì)先執(zhí)行我?。?); } @AfterReturning("test()") public void after(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); //獲取方法入?yún)? System.out.println("原方法執(zhí)行后會(huì)執(zhí)行我?。?); } }
如果遇到異常,則不執(zhí)行。
- 當(dāng)連接點(diǎn)方法成功執(zhí)行后,返回通知方法才會(huì)執(zhí)行,如果連接點(diǎn)方法出現(xiàn)異常,則返回通知方法不執(zhí)行。
- 返回通知方法在目標(biāo)方法執(zhí)行成功后才會(huì)執(zhí)行,所以,返回通知方法可以拿到目標(biāo)方法(連接點(diǎn)方法)執(zhí)行后的結(jié)果。
@AfterReturning獲取被切方法返回值,篡改返回參數(shù):
在注解中增加returning參數(shù)即可: returning = “methodResult”
@Pointcut("execution(* com.daji.aop_test.AopTestController.test1(..))") public void publish() { } @AfterReturning(value = "publish()",returning = "methodResult") public Object afterReturningPublish(JoinPoint joinPoint, Object methodResult) { //獲取方法返回值 String returnJson = JSONObject.toJSONString(methodResult); Object[] args = joinPoint.getArgs(); System.out.println("原方法執(zhí)行后會(huì)執(zhí)行我??!"); //這個(gè)返回值可以被我們篡改。 return methodResult; }
其實(shí)這個(gè)返回值也不是能任意篡改的:
- 答案來(lái)了:可以改變返回值,但是分情況,
不能改變:
- 第一種情況:如果返回的對(duì)象,改變了對(duì)象的引用地址,這種情況,是不能改變返回對(duì)象中的值的
- 第二種情況:如果返回的對(duì)象是一個(gè)基本數(shù)據(jù)類型,或者是String的值,是不能改變返回值的,尤其是String這種final類型的。
可以改變:
- 直接使用傳入的object對(duì)象,改變其中的值,是可以的。
2、@Around
@Around是環(huán)繞通知,既可以控制入?yún)?,還可以控制原方法的執(zhí)行和返回值
常常配合ProceedingJoinPoint來(lái)使用。
直接看例子:
被切的方法:
@Service public class OriFuncImpl implements OriFunc{ @Override public String ori(String str){ System.out.println("執(zhí)行了原方法"); return str; } }
使用@Around:
@Aspect @Component public class AopFunc { @Pointcut("execution(* com.daji.aop_test.OriFuncImpl.ori(..))") public void modifyReturn() { } @Around("modifyReturn()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { Object[] args = joinPoint.getArgs(); Object result = joinPoint.proceed(args); return result; } }
3、@Around可以篡改返回值,篡改入?yún)?/h3>
需要ProceedingJoinPoint的配合
注意一定要將@Around修飾的方法用Object修飾其返回值,并且返回原方法執(zhí)行的結(jié)果,如下圖所示:
篡改入?yún)⒁粯拥牡览恚恍枰鄹南聢D中的 args數(shù)組,然后讓其傳入proceed中,即可完成篡改入?yún)ⅰ?/p>
如下圖所示:
所以,這個(gè)@Around比較萬(wàn)能,尤其是配合ProceedingJoinPoint的使用。使AOP能做的事情更多了。
引申一下JoinPoint 和 ProceedingJoinPoint的關(guān)系:
- ProceedingJoinPoint 只能在@Around中使用
- JoinPoint也可以獲取入?yún)ⅲ╣etArgs()),它可以用于@Before 和 @AfterReturning
- Proceedingjoinpoint 繼承了 JoinPoint 。是在JoinPoint的基礎(chǔ)上暴露出 proceed 這個(gè)方法。它們之間的關(guān)系如下圖:
4、@Around如果不執(zhí)行proceed(),那么原方法將不會(huì)執(zhí)行
二、使用AOP常見(jiàn)的問(wèn)題和bug
1、切不進(jìn)去
檢查是否有如下注解:
<dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.9.4</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.2</version> </dependency>
檢查完畢后檢查切面類,看看有沒(méi)有以下注解:
2、ctrl+鼠標(biāo)左鍵不能自動(dòng)跳到被切方法
正常情況如圖:
如果你存在上述問(wèn)題,檢查你有沒(méi)有安裝下列插件:
如果你是idea社區(qū)版,那么默認(rèn)是沒(méi)有的,你要么自己下,要么換成正式版。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
使用Java代碼實(shí)現(xiàn)RocketMQ的生產(chǎn)與消費(fèi)消息
這篇文章介紹一下其他的小組件以及使用Java代碼實(shí)現(xiàn)生產(chǎn)者對(duì)消息的生成,消費(fèi)者消費(fèi)消息等知識(shí)點(diǎn),并通過(guò)代碼示例介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-07-07springboot 場(chǎng)景啟動(dòng)器使用解析
這篇文章主要介紹了springboot 場(chǎng)景啟動(dòng)器使用解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02java教程之對(duì)象序列化使用基礎(chǔ)示例詳解
所謂對(duì)象序列化就是將對(duì)象的狀態(tài)轉(zhuǎn)換成字節(jié)流,以后可以通過(guò)這些值再生成相同狀態(tài)的對(duì)象,下面詳細(xì)介紹一下java對(duì)象的序列化使用方法2014-01-01SpringBoot+Redis實(shí)現(xiàn)接口防刷的示例代碼
在實(shí)際開(kāi)發(fā)中,會(huì)出現(xiàn)用戶多次點(diǎn)擊發(fā)送請(qǐng)求,本文主要介紹了SpringBoot+Redis實(shí)現(xiàn)接口防刷的示例代碼,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01解析Java的InputStream類并借助其讀取ppt文件
這篇文章主要介紹了Java的InputStream類并借助其讀取ppt文件,講到了InputStream類中一些常用的方法的問(wèn)題,需要的朋友可以參考下2015-11-11利用SpringDataJPA開(kāi)啟審計(jì)功能,自動(dòng)保存操作人操作時(shí)間
這篇文章主要介紹了利用SpringDataJPA開(kāi)啟審計(jì)功能,自動(dòng)保存操作人操作時(shí)間,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12