SpringBoot基礎篇AOP之基本使用姿勢小結
一般來講,談到Spring的特性,繞不過去的就是DI(依賴注入)和AOP(切面),在將bean的系列中,說了DI的多種使用姿勢;接下來看一下AOP的玩法
<!-- more -->
I. 背景知識
在實際使用之前有必要了解一下什麼是AOP,以及AOP的幾個基本概念
1. advice
- before: 在方法執行之前被呼叫
- 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
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-cloud.version>Finchley.RELEASE</spring-cloud.version>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</pluginManagement>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
2. 程式碼準備
首先建立一個被攔截的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
知識星球
相關推薦
SpringBoot基礎篇AOP之基本使用姿勢小結
浪費了“黃金五年”的Java程式設計師,還有救嗎? >>>
SpringBoot基礎篇Bean之基本定義與使用
我們知道在Spring中,有兩個非常有名的特性,依賴注入(DI)與切面(AOP),其中依賴注入其主要的作用,可以說就是維護Spring容器建立的Bean之間的依賴關係,簡單來說就是一個bean(假定名為A)持有另一個Bean(假定名為B)的引用作為成員變數b,
SpringBoot基礎篇AOP之高階使用技能
開發十年,就只剩下這套架構體系了! >>>
SpringBoot基礎篇Bean之條件注入@Condition使用姿勢
前面幾篇關於Bean的基礎博文中,主要集中在Bean的定義和使用,但實際的情況中有沒有一些場景是不載入我定義的bean,或者只有滿足某些前提條件的時候才載入我定義的Bean呢? 本篇博文將主要介紹bean的載入中,條件註解@Conditional的相關使用 I. @Con
SpringBoot基礎篇Bean之自動載入
前面一篇介紹了Bean的常用姿勢,在一個專案中,可能不會出現什麼問題,可如果你提供了一個Jar包供第三方使用者使用,那麼你這個jar包中的Bean,能被第三方載入麼? 本篇博文將主要介紹AutoConfig相關的內容,即如果我想提供一個jar包供第三方在Spr
SpringCloud基礎篇AOP之攔截優先順序詳解
開發十年,就只剩下這套架構體系了! >>>
SpringBoot基礎篇配置資訊之如何讀取配置資訊
SpringBoot極大的減少了配置,開一個新專案時,完全可以做到什麼配置都不加,就可以直接跑,簡單方便的同時,就帶來了一個問題 怎麼知道這些預設的配置是什麼? 如果要修改預設配置怎麼辦? 如何新增自定義的配置? 如何讀取這些配置? I. 配置資訊讀取 首
SpringBoot基礎篇配置資訊之多環境配置資訊
前面一篇主要介紹的是如何獲取配置資訊,接下來則是另外一個非常非常基礎和必要的知識點了,應用如何根據不同的環境來選擇對應的配置,即配置的多環境選擇問題 I. 多環境配置 配置區分環境,最直觀的如測試環境和生產環境的DB不同,測試環境的應用要求連線測試DB;生成
SpringBoot基礎篇之重名Bean的解決與多實例選擇
choose r.java console 自動 turn tin pri nested port 當通過接口的方式註入Bean時,如果有多個子類的bean存在時,具體哪個bean會被註入呢?系統中能否存在兩個重名的bean呢?如果可以,那麽怎麽選擇引入呢?如果不行的話又該
《Python基礎篇》之初識Python一
file dff lam lag port nag elong dir car %E4%BD%BF%E7%94%A8CHttpFile%E4%BB%8E%E6%9C%8D%E5%8A%A1%E5%99%A8%E7%AB%AF%E6%AD%A3%E7%A1%AE%E7%9A%
Linux 磁盤管理 基礎篇--創建基本文件分區
分區 文件系統 掛載 1 概述本文通過對講解了磁盤的基本概念,分區,創建文件系統,掛載,卸載等步驟進行講解。使得用戶能夠創建基本的磁盤並掛載使用空間。2 磁盤基本概念2.1 磁盤空間每個扇區大小是512byte,這是磁盤的最小單位每一個磁道上有多少個扇區就是扇區數 sectors每一圈叫做磁道(t
[ 轉載 ] Java多線程系列--“基礎篇”04之 synchronized關鍵字
span 多線程 sync body 關鍵字 .com style 基礎 pos http://www.cnblogs.com/skywang12345/p/3479202.html[ 轉載 ] Java多線程系列--“基礎篇”04之 synchronized關鍵字
申論(基礎篇)之基礎常識復習建議
地形 pos 基礎 ima clas 技術 相關 歷史人物 復習 一、歷史常識復習建議(1~3題) 以歷史人物為中心學習相關的歷史事實、常識。 二、地理知識復習建議(1~3題) 1、自轉和公轉 2、中國三級階梯 3、
Java多線程系列---“基礎篇”04之 synchronized關鍵字
www. 時間 pub syn pre 現在 執行流程 什麽 你是 轉自:https://www.cnblogs.com/skywang12345/p/3479202.html (含部分修改) 概要 本章,會對synchronized關鍵字進行介紹。涉及到的內容包括:
Java多線程系列---“基礎篇”07之 線程休眠
技術分享 ring 說明 href super 等於 在線 for logs 轉自:http://www.cnblogs.com/skywang12345/p/3479256.html (含部分修改) 概要 本章,會對Thread中sleep()方法進行介紹。涉及到的內
Java多線程系列---“基礎篇”08之 join()
spa www gif 怎麽 ... try run stack 通過 轉自:http://www.cnblogs.com/skywang12345/p/3479275.html (含部分修改) 概要 本章,會對Thread中join()方法進行介紹。涉及到的內容包括:
JAVA常用集合框架用法詳解基礎篇一之Colletion介面
首先,在學習集合之前我們能夠使用的可以儲存多個元素的容器就是陣列。 下面舉幾個例子主要是引出集合類的: 1、8,4,5,6,7,55,7,8 像這樣的型別相同的可以使用陣列來儲存,本例可以用int[] arr來儲存。 2、”zhnagsan”,true,68 像這樣的可以使
JAVA常用集合框架用法詳解基礎篇三之Colletion子介面Set
這一篇我們來介紹Collection介面的另一個子介面,Set介面。Set是個介面,元素不可以重複,是無序的。Set介面中的方法和Collection的一致。 A、Set的子類: 1、HashSet:此類實現的Set介面,由雜湊表(實際上是一個HashMap)例項支援,它不保證Set的迭代順
JAVA常用集合框架用法詳解基礎篇二之Colletion子介面List
接著上一篇,接著講講集合的知識。上一篇講了Collection介面。它可以說是集合的祖先了,我們這一篇就說說它的子孫們。 一、Collection的子介面 List:有序(存入和取出的順序一致),元素都有索引(即角標),元素可以重複。 Set:元素不能重複,無序的。 首先講講L
Java多線程系列---“基礎篇”13之 樂觀鎖與悲觀鎖
而是 關系型 lock color 情況 發現 mis 再次 中一 轉自:http://www.cnblogs.com/zhengbin/p/5657435.html 樂觀鎖 樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿數據的時候都認