摘要:通知和切點共同定義了關于切面的全部內容,它是什么時候,在何時和何處完成功能引入允許我們向現有的類添加新的方法或者屬性組裝方面來創建一個被通知對象。這可以在編譯時完成例如使用編譯器,也可以在運行時完成。和其他純框架一樣,在運行時完成織入。
原文:190301-SpringBoot基礎篇AOP之基本使用姿勢小結
一般來講,談到Spring的特性,繞不過去的就是DI(依賴注入)和AOP(切面),在將bean的系列中,說了DI的多種使用姿勢;接下來看一下AOP的玩法
I. 背景知識在實際使用之前有必要了解一下什么是AOP,以及AOP的幾個基本概念
1. advicebefore: 在方法執行之前被調用
after: 在方法執行之后調用
after returning: 方法執行成功之后
after throwing: 方法拋出異常之后
around: 環繞,自己在內部決定方法的執行時機,因此可以在之前之后做一些業務邏輯
2. join point連接點,比如方法調用,方法執行,字段設置/獲取、異常處理執行、類初始化、甚至是 for 循環中的某個點
但 Spring AOP 目前僅支持方法執行 (method execution)
簡單來說,Spring AOP中,PointCut就是那個被攔截的方法
3. pointcut切點,用來描述滿足什么規則的方法會被攔截
正則表達式 : @Before("execution(public * com.git.hui.demo.base.bean.*.*(..))")
注解攔截方式 :@Around("@annotation(parameterCheck)")
4. aspect切面是切點和通知的結合。通知和切點共同定義了關于切面的全部內容,它是什么時候,在何時和何處完成功能
5. introduction引入允許我們向現有的類添加新的方法或者屬性
6. weaving組裝方面來創建一個被通知對象。這可以在編譯時完成(例如使用AspectJ編譯器),也可以在運行時完成。Spring和其他純Java AOP框架一樣,在運行時完成織入。
簡單來講就是生成一個代理類,在調用被攔截的方法時,實際上執行的是代理類,這個代理類內部執行切面邏輯
II. 使用說明 1. 基本配置首先是基本環境的搭建, 先貼上必要的xml配置, 使用aop需要引入包: spring-boot-starter-aop
2. 代碼準備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
首先創建一個被攔截的bean: com.git.hui.boot.aop.demo.DemoBean,如下
@Component public class DemoBean { /** * 返回隨機的字符串 * * @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!"); } } }
接著在啟動類中,執行
@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使用
在實際使用之前,需要創建一個切面,用@Aspect聲明,其次切面也需要作為bean托付給Spring容器管理
@Aspect @Component public class AnoAspcet { }a. before
在方法調用之前,需要執行一些操作,這個時候可以使用 @Before 注解來聲明before advice
一種可使用姿勢如下,我們的切點直接在注解中進行定義,使用正則表達式的方式
@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
在方法調用完畢之后,再執行一些操作,這個時候after就可以派上用場,為了考慮切點的通用性,我們可以考慮聲明一個切點,使用@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
在正常返回結果之后,再次執行,這個也挺有意思的,通常使用這個advice時,一般希望獲取返回結果,那么應該怎么處理呢?
org.aspectj.lang.annotation.AfterReturning#returning 指定返回結果對應參數name
返回結果作為參數傳入,要求類型一致,否則不生效
/** * 執行完畢之后,通過 args指定參數;通過 returning 指定返回的結果,要求返回值類型匹配 * * @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
這個也比較常見,在方法執行前后干一些事情,比如常見的耗時統計,日志打印,安全控制等,很多都是基于around advice實現的
使用這個advice需要注意的是傳入參數類型為 ProceedingJoinPoint,需要在方法內部顯示執行org.aspectj.lang.ProceedingJoinPoint#proceed()來表示調用方法
@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. 輸出
執行之后輸出如下
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---------
從輸出結果上,可以看到每個advice的使用范圍,當然也帶來了一些疑問
可以存在多個同類型的advice,攔截同一個目標嗎?(如兩個around都攔截methodA方法,那么methodA方法被調用時,兩個around advice是否都會執行)
多個advice之間的優先級怎么定義?
aop攔截的目標方法有沒有限制(對非public的方法可以攔截么?)
被攔截的方法中存在相互調用的時候,會怎樣?(如methodA,methodB都可以被攔截,且methodA中調用了methodB,那么在執行methodA時,methodB的各種advice是否會被觸發?)
基于注解的aop方式可以怎樣用
以上這些問題留在下一篇進行介紹
III. 其他 0. 項目工程:https://github.com/liuyueyi/spring-boot-demo
項目: https://github.com/liuyueyi/spring-boot-demo/tree/master/spring-boot/010-aop
1. 一灰灰Blog一灰灰Blog個人博客 https://blog.hhui.top
一灰灰Blog-Spring專題博客 http://spring.hhui.top
一灰灰的個人博客,記錄所有學習和工作中的博文,歡迎大家前去逛逛
2. 聲明盡信書則不如,以上內容,純屬一家之言,因個人能力有限,難免有疏漏和錯誤之處,如發現bug或者有更好的建議,歡迎批評指正,不吝感激
微博地址: 小灰灰Blog
QQ: 一灰灰/3302797840
3. 掃描關注一灰灰blog
知識星球
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/19468.html
摘要:時間年月日星期日說明本文部分內容均來自慕課網。慕課網教學示例源碼個人學習源碼第一章課程介紹課程介紹本課程緊接著小時學會課程,請先看入門課。異常返回通知在連接點拋出異常后執行。 時間:2017年3月19日星期日說明:本文部分內容均來自慕課網。@慕課網:http://www.imooc.com教學示例源碼:https://github.com/zccodere/s...個人學習源碼:htt...
摘要:原文高級篇之修改基本使用姿勢本篇依然是中的一篇,主要介紹的更新,主要內容如下常見類型成員的修改數組類型成員的增刪改類型成員的增刪改基本使用首先是準備好基本環境,可以參考博文高級篇之基本環境搭建與使用高級篇之查詢基本使用姿勢在開 原文: 190218-SpringBoot高級篇MongoDB之修改基本使用姿勢 本篇依然是MongoDB curd中的一篇,主要介紹document的更新,...
摘要:總結動態代理的相關原理已經講解完畢,接下來讓我們回答以下幾個思考題。 【干貨點】 此處是【好好面試】系列文的第12篇文章。文章目標主要是通過原理剖析的方式解答Aop動態代理的面試熱點問題,通過一步步提出問題和了解原理的方式,我們可以記得更深更牢,進而解決被面試官卡住喉嚨的情況。問題如下 SpringBoot默認代理類型是什么 為什么不用靜態代理 JDK動態代理原理 CGLIB動態代理...
閱讀 2113·2021-11-16 11:45
閱讀 1184·2021-10-22 09:53
閱讀 4002·2021-09-07 10:26
閱讀 1209·2021-09-06 15:00
閱讀 2073·2019-08-28 18:09
閱讀 2795·2019-08-26 14:06
閱讀 3934·2019-08-26 13:48
閱讀 1296·2019-08-26 12:11