摘要:通知和切點(diǎn)共同定義了關(guān)于切面的全部內(nèi)容,它是什么時(shí)候,在何時(shí)和何處完成功能引入允許我們向現(xiàn)有的類添加新的方法或者屬性組裝方面來創(chuàng)建一個(gè)被通知對象。這可以在編譯時(shí)完成例如使用編譯器,也可以在運(yùn)行時(shí)完成。和其他純框架一樣,在運(yùn)行時(shí)完成織入。
原文:190301-SpringBoot基礎(chǔ)篇AOP之基本使用姿勢小結(jié)
一般來講,談到Spring的特性,繞不過去的就是DI(依賴注入)和AOP(切面),在將bean的系列中,說了DI的多種使用姿勢;接下來看一下AOP的玩法
I. 背景知識(shí)在實(shí)際使用之前有必要了解一下什么是AOP,以及AOP的幾個(gè)基本概念
1. advicebefore: 在方法執(zhí)行之前被調(diào)用
after: 在方法執(zhí)行之后調(diào)用
after returning: 方法執(zhí)行成功之后
after throwing: 方法拋出異常之后
around: 環(huán)繞,自己在內(nèi)部決定方法的執(zhí)行時(shí)機(jī),因此可以在之前之后做一些業(yè)務(wù)邏輯
2. join point連接點(diǎn),比如方法調(diào)用,方法執(zhí)行,字段設(shè)置/獲取、異常處理執(zhí)行、類初始化、甚至是 for 循環(huán)中的某個(gè)點(diǎn)
但 Spring AOP 目前僅支持方法執(zhí)行 (method execution)
簡單來說,Spring AOP中,PointCut就是那個(gè)被攔截的方法
3. pointcut切點(diǎn),用來描述滿足什么規(guī)則的方法會(huì)被攔截
正則表達(dá)式 : @Before("execution(public * com.git.hui.demo.base.bean.*.*(..))")
注解攔截方式 :@Around("@annotation(parameterCheck)")
4. aspect切面是切點(diǎn)和通知的結(jié)合。通知和切點(diǎn)共同定義了關(guān)于切面的全部內(nèi)容,它是什么時(shí)候,在何時(shí)和何處完成功能
5. introduction引入允許我們向現(xiàn)有的類添加新的方法或者屬性
6. weaving組裝方面來創(chuàng)建一個(gè)被通知對象。這可以在編譯時(shí)完成(例如使用AspectJ編譯器),也可以在運(yùn)行時(shí)完成。Spring和其他純Java AOP框架一樣,在運(yùn)行時(shí)完成織入。
簡單來講就是生成一個(gè)代理類,在調(diào)用被攔截的方法時(shí),實(shí)際上執(zhí)行的是代理類,這個(gè)代理類內(nèi)部執(zhí)行切面邏輯
II. 使用說明 1. 基本配置首先是基本環(huán)境的搭建, 先貼上必要的xml配置, 使用aop需要引入包: spring-boot-starter-aop
2. 代碼準(zhǔn)備org.springframework.boot spring-boot-starter-parent 2.0.4.RELEASE UTF-8 UTF-8 Finchley.RELEASE 1.8 org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-maven-plugin spring-milestones Spring Milestones https://repo.spring.io/milestone false
首先創(chuàng)建一個(gè)被攔截的bean: com.git.hui.boot.aop.demo.DemoBean,如下
@Component public class DemoBean { /** * 返回隨機(jī)的字符串 * * @param time * @return */ public String randUUID(long time) { try { System.out.println("in randUUID before process!"); return UUID.randomUUID() + "|" + time; } finally { System.out.println("in randUUID finally!"); } } }
接著在啟動(dòng)類中,執(zhí)行
@SpringBootApplication public class Application { public Application(DemoBean demoBean) { String ans = demoBean.randUUID(System.currentTimeMillis()); System.out.println("----- ans: " + ans + "---------"); } public static void main(String[] args) { SpringApplication.run(Application.class); } }3. AOP使用
在實(shí)際使用之前,需要?jiǎng)?chuàng)建一個(gè)切面,用@Aspect聲明,其次切面也需要作為bean托付給Spring容器管理
@Aspect @Component public class AnoAspcet { }a. before
在方法調(diào)用之前,需要執(zhí)行一些操作,這個(gè)時(shí)候可以使用 @Before 注解來聲明before advice
一種可使用姿勢如下,我們的切點(diǎn)直接在注解中進(jìn)行定義,使用正則表達(dá)式的方式
@Before("execution(public * com.git.hui.boot.aop.demo.*.*(*))") public void doBefore(JoinPoint joinPoint) { System.out.println("do in Aspect before method called! args: " + JSON.toJSONString(joinPoint.getArgs())); }b. after
在方法調(diào)用完畢之后,再執(zhí)行一些操作,這個(gè)時(shí)候after就可以派上用場,為了考慮切點(diǎn)的通用性,我們可以考慮聲明一個(gè)切點(diǎn),使用@Pointcut注解
@Pointcut("execution(public * com.git.hui.boot.aop.demo.*.*(*))") public void point() { }
使用pointcut的方式也比較簡單,如下
@After("point()") public void doAfter(JoinPoint joinPoint) { System.out.println("do in Aspect after method called! args: " + JSON.toJSONString(joinPoint.getArgs())); }c. after returning
在正常返回結(jié)果之后,再次執(zhí)行,這個(gè)也挺有意思的,通常使用這個(gè)advice時(shí),一般希望獲取返回結(jié)果,那么應(yīng)該怎么處理呢?
org.aspectj.lang.annotation.AfterReturning#returning 指定返回結(jié)果對應(yīng)參數(shù)name
返回結(jié)果作為參數(shù)傳入,要求類型一致,否則不生效
/** * 執(zhí)行完畢之后,通過 args指定參數(shù);通過 returning 指定返回的結(jié)果,要求返回值類型匹配 * * @param time * @param result */ @AfterReturning(value = "point() && args(time)", returning = "result") public void doAfterReturning(long time, String result) { System.out.println("do in Aspect after method return! args: " + time + " ans: " + result); }d. around
這個(gè)也比較常見,在方法執(zhí)行前后干一些事情,比如常見的耗時(shí)統(tǒng)計(jì),日志打印,安全控制等,很多都是基于around advice實(shí)現(xiàn)的
使用這個(gè)advice需要注意的是傳入?yún)?shù)類型為 ProceedingJoinPoint,需要在方法內(nèi)部顯示執(zhí)行org.aspectj.lang.ProceedingJoinPoint#proceed()來表示調(diào)用方法
@Around("point()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("do in Aspect around ------ before"); Object ans = joinPoint.proceed(); System.out.println("do in Aspect around ------- over! ans: " + ans); return ans; }e. 輸出
執(zhí)行之后輸出如下
do in Aspect around ------ before do in Aspect before method called! args: [1551433188205] in randUUID before process! in randUUID finally! do in Aspect around ------- over! ans: 6849544b-160e-464c-80bd-641f2651c6c1|1551433188205 do in Aspect after method called! args: [1551433188205] do in Aspect after method return! args: 1551433188205 ans: 6849544b-160e-464c-80bd-641f2651c6c1|1551433188205 ----- ans: 6849544b-160e-464c-80bd-641f2651c6c1|1551433188205---------
從輸出結(jié)果上,可以看到每個(gè)advice的使用范圍,當(dāng)然也帶來了一些疑問
可以存在多個(gè)同類型的advice,攔截同一個(gè)目標(biāo)嗎?(如兩個(gè)around都攔截methodA方法,那么methodA方法被調(diào)用時(shí),兩個(gè)around advice是否都會(huì)執(zhí)行)
多個(gè)advice之間的優(yōu)先級(jí)怎么定義?
aop攔截的目標(biāo)方法有沒有限制(對非public的方法可以攔截么?)
被攔截的方法中存在相互調(diào)用的時(shí)候,會(huì)怎樣?(如methodA,methodB都可以被攔截,且methodA中調(diào)用了methodB,那么在執(zhí)行methodA時(shí),methodB的各種advice是否會(huì)被觸發(fā)?)
基于注解的aop方式可以怎樣用
以上這些問題留在下一篇進(jìn)行介紹
III. 其他 0. 項(xiàng)目工程:https://github.com/liuyueyi/spring-boot-demo
項(xiàng)目: https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/010-aop
1. 一灰灰Blog一灰灰Blog個(gè)人博客 https://blog.hhui.top
一灰灰Blog-Spring專題博客 http://spring.hhui.top
一灰灰的個(gè)人博客,記錄所有學(xué)習(xí)和工作中的博文,歡迎大家前去逛逛
2. 聲明盡信書則不如,以上內(nèi)容,純屬一家之言,因個(gè)人能力有限,難免有疏漏和錯(cuò)誤之處,如發(fā)現(xiàn)bug或者有更好的建議,歡迎批評指正,不吝感激
微博地址: 小灰灰Blog
QQ: 一灰灰/3302797840
3. 掃描關(guān)注一灰灰blog
知識(shí)星球
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/19468.html
摘要:時(shí)間年月日星期日說明本文部分內(nèi)容均來自慕課網(wǎng)。慕課網(wǎng)教學(xué)示例源碼個(gè)人學(xué)習(xí)源碼第一章課程介紹課程介紹本課程緊接著小時(shí)學(xué)會(huì)課程,請先看入門課。異常返回通知在連接點(diǎn)拋出異常后執(zhí)行。 時(shí)間:2017年3月19日星期日說明:本文部分內(nèi)容均來自慕課網(wǎng)。@慕課網(wǎng):http://www.imooc.com教學(xué)示例源碼:https://github.com/zccodere/s...個(gè)人學(xué)習(xí)源碼:htt...
摘要:原文高級(jí)篇之修改基本使用姿勢本篇依然是中的一篇,主要介紹的更新,主要內(nèi)容如下常見類型成員的修改數(shù)組類型成員的增刪改類型成員的增刪改基本使用首先是準(zhǔn)備好基本環(huán)境,可以參考博文高級(jí)篇之基本環(huán)境搭建與使用高級(jí)篇之查詢基本使用姿勢在開 原文: 190218-SpringBoot高級(jí)篇MongoDB之修改基本使用姿勢 本篇依然是MongoDB curd中的一篇,主要介紹document的更新,...
摘要:總結(jié)動(dòng)態(tài)代理的相關(guān)原理已經(jīng)講解完畢,接下來讓我們回答以下幾個(gè)思考題。 【干貨點(diǎn)】 此處是【好好面試】系列文的第12篇文章。文章目標(biāo)主要是通過原理剖析的方式解答Aop動(dòng)態(tài)代理的面試熱點(diǎn)問題,通過一步步提出問題和了解原理的方式,我們可以記得更深更牢,進(jìn)而解決被面試官卡住喉嚨的情況。問題如下 SpringBoot默認(rèn)代理類型是什么 為什么不用靜態(tài)代理 JDK動(dòng)態(tài)代理原理 CGLIB動(dòng)態(tài)代理...
閱讀 2150·2021-11-16 11:45
閱讀 1278·2021-10-22 09:53
閱讀 4046·2021-09-07 10:26
閱讀 1249·2021-09-06 15:00
閱讀 2101·2019-08-28 18:09
閱讀 2844·2019-08-26 14:06
閱讀 4033·2019-08-26 13:48
閱讀 1332·2019-08-26 12:11