Spring AOP在專案中的典型應用場景

語言: CN / TW / HK

學過 Spring 的小夥伴相信都知道 AOP,AOP 學的好的小夥伴相信對 AOP 的概念也是輕車熟路:面向切面程式設計、切點、切面、通知,Aspect、Pointcut、Advice 等如數家珍。

AOP 之所以這麼重要,是因為它在專案中有著非常廣泛的應用,今天這篇文章,鬆哥就來和大家總結一下,我們在日常開發中,都有哪些典型場景需要用到 AOP。

> 先來一句話總結下,AOP 的使用,基本上都會涉及到自定義註解,一個非常常見的組合,就是自定義註解+AOP。

在日常的開發中,有很多重複的程式碼,我們總是希望將之簡化,AOP 就是一個非常常用的簡化手段。簡化的思路一般是這樣:

  1. 首先,自定義一個註解。
  2. 定義 AOP 切面,在切面中,定義切點和通知,切點,也就是方法的攔截規則,我們可以按照註解來攔截,也就是某一個帶有自定義註解的方法,將被我攔截下來。
  3. 攔截下來之後,前置通知、後置通知、異常通知、返回通知還是環繞通知,就可以隨便寫了。

所以,這些涉及到自定義註解的地方,基本上都可以算是 AOP 的使用場景了,因為自定義註解,需要用 AOP 來解析。

接下來我們來看幾個比較典型的例子。

1. 冪等性處理

介面冪等性的處理,其實有很多種不同的方案,例如:

  1. Token 機制
  2. 去重表
  3. 利用 Redis 的 setnx
  4. 設定狀態欄位
  5. 上鎖

無論是哪種方案處理冪等性,每個方法裡邊都去寫一遍冪等性的處理顯然是不現實的,因此,一般都是將冪等性的處理通過自定義註解+AOP給封裝起來,大致的思路如下:

  1. 首先自定義一個註解。
  2. 自定義切點,攔截所有加了自定義註解的方法。
  3. 定義環繞通知,在環繞通知中,先通過上述五種思路中的任意一種,對方法執行的冪等性進行判斷,判斷通過了,再執行目標方法,判斷不通過,則直接丟擲異常,不執行目標方法。

這就是自定義註解+AOP 的一個典型應用場景。

如果你對上面的表述雲裡霧裡,不妨看看鬆哥之前發的這個視訊,有詳細的手把手教程:處理介面冪等性的兩種常見方案|手把手教你

2. 介面限流

對於介面限流,目前來說,一個比較成熟的方案是使用 Alibaba 的 Sentienl,簡單配置一下就可以實現介面限流了。

但是如果沒有用這個工具呢?如果是我們自己寫呢?毫無疑問,還是自定義註解+AOP,思路大致如下:

  1. 自定義註解。
  2. 在需要進行限流的介面方法上新增自定義註解,同時還可以設定一些限流的引數,例如時間視窗值、流量大小等。
  3. 自定義切點,攔截規則就是所有添加了自定義註解的方法,攔截到方法之後,在環繞通知中,可以通過 Redis 外掛 redis-cell、通過漏斗演算法去處理限流,這個我這裡就不羅嗦了,之前的文章中都寫過了。限流計算沒問題的話,就執行目標方法,否則將操作攔截下來。

大致思路如上,說白了就是自定義註解+ AOP,道理雖然簡單,但是真正做起來,還是有很多細節,感興趣的小夥伴可以參考鬆哥之前的這篇文章:Redis 做介面限流,一個註解的事!

3. 日誌處理

說到 AOP,所有人都能想到的使用場景了,這個我就不羅嗦了,鬆哥之前也有過專門的文章介紹,沒看過的小夥伴們戳這裡:記錄專案日誌,一個註解搞定

4. 多資料來源處理

有時候我們專案中存在多個不同的資料來源,在實際使用中需要進行切換,網上也有一些開源的解決方案,不過這個東西其實並不難,我們也可以自己寫。

自定義多資料來源的處理,大致上思路如下:

從 Spring2.0.1 中引入了 AbstractRoutingDataSource 類,(注意是 Spring2.0.1 不是 Spring Boot2.0.1,所以這其實也算是 Spring 一個非常古老的特性了), 該類充當了 DataSource 的路由中介,它能夠在執行時, 根據某種 key 值來動態切換到真正的 DataSource 上。

大致的用法就是你提前準備好各種資料來源,存入到一個 Map 中,Map 的 key 就是這個資料來源的名字,Map 的 value 就是這個具體的資料來源,然後再把這個 Map 配置到 AbstractRoutingDataSource 中,最後,每次執行資料庫查詢的時候,拿一個 key 出來,AbstractRoutingDataSource 會找到具體的資料來源去執行這次資料庫操作。

基於以上知識,我們可以自定義一個註解,在需要切換資料來源的方法上,新增這個註解,然後通過 AOP 去解析這個自定義註解,當目標方法被攔截下來的時候,我們跟進註解中的配置,重新設定要執行的資料來源,這樣將來 service 中的方法在執行的過程中,就會使用到切換之後的資料來源了。

思路並不難,鬆哥之前也寫過詳細的教程,小夥伴們參考這裡:

5. 方法許可權處理

這個其實也跟前面的差不多。

方法級別的許可權處理,一般來說也是基於註解來完成的。如果你使用了 Spring Security 之類的許可權框架,就不用自己解析許可權註解了,按照框架的要求直接來使用就行了。

有的時候,我們可能沒有使用 Spring Security,想自己處理許可權註解,也是可以的。使用者自定義許可權註解,為註解新增屬性,然後將註解新增到目標方法上,再通過 AOP 去解析這個註解,AOP 將目標方法的執行攔截下來,然後判斷使用者是否具備所需要的許可權,如果具備,就執行目標方法,否則就不執行。

前兩天鬆哥剛剛分享的在微服務中,服務內部的許可權校驗,就是自定義一個註解,將從其他微服務上來的請求給攔截下來,然後判斷請求的來源,如果是從其他微服務上來的,就執行目標方法,如果不是從其他微服務上來的,而是從外部來的請求,那麼就將之攔截下來丟擲異常,不執行目標方法,參見:微服務中的鑑權該怎麼做?

6. 事務處理

這個倒是不需要自定義註解,對於宣告式事務,直接用現成的註解就行了,但是本質上也是 AOP,如果有小夥伴在 Spring 的 XML 中配置過事務的話,就知道這個東西底層也是 AOP。

好啦,梳理了幾個簡單的案例,希望小夥伴們瞭解到 AOP 並不是屠龍術,而是在日常開發中有著廣泛應用的技術。