Spring AOP在項(xiàng)目中的典型應(yīng)用場(chǎng)景
學(xué)過(guò) Spring 的小伙伴相信都知道 AOP,AOP 學(xué)的好的小伙伴相信對(duì) AOP 的概念也是輕車熟路:面向切面編程、切點(diǎn)、切面、通知,Aspect、Pointcut、Advice 等如數(shù)家珍。
AOP 之所以這么重要,是因?yàn)樗陧?xiàng)目中有著非常廣泛的應(yīng)用,今天這篇文章,松哥就來(lái)和大家總結(jié)一下,我們?cè)谌粘i_(kāi)發(fā)中,都有哪些典型場(chǎng)景需要用到 AOP。
先來(lái)一句話總結(jié)下,AOP 的使用,基本上都會(huì)涉及到自定義注解,一個(gè)非常常見(jiàn)的組合,就是自定義注解+AOP。
在日常的開(kāi)發(fā)中,有很多重復(fù)的代碼,我們總是希望將之簡(jiǎn)化,AOP 就是一個(gè)非常常用的簡(jiǎn)化手段。簡(jiǎn)化的思路一般是這樣:
- 首先,自定義一個(gè)注解。
- 定義 AOP 切面,在切面中,定義切點(diǎn)和通知,切點(diǎn),也就是方法的攔截規(guī)則,我們可以按照注解來(lái)攔截,也就是某一個(gè)帶有自定義注解的方法,將被我攔截下來(lái)。
- 攔截下來(lái)之后,前置通知、后置通知、異常通知、返回通知還是環(huán)繞通知,就可以隨便寫(xiě)了。
所以,這些涉及到自定義注解的地方,基本上都可以算是 AOP 的使用場(chǎng)景了,因?yàn)樽远x注解,需要用 AOP 來(lái)解析。
接下來(lái)我們來(lái)看幾個(gè)比較典型的例子。
1. 冪等性處理
接口冪等性的處理,其實(shí)有很多種不同的方案,例如:
- Token 機(jī)制
- 去重表
- 利用 Redis 的 setnx
- 設(shè)置狀態(tài)字段
- 上鎖
無(wú)論是哪種方案處理冪等性,每個(gè)方法里邊都去寫(xiě)一遍冪等性的處理顯然是不現(xiàn)實(shí)的,因此,一般都是將冪等性的處理通過(guò)自定義注解+AOP給封裝起來(lái),大致的思路如下:
首先自定義一個(gè)注解。
自定義切點(diǎn),攔截所有加了自定義注解的方法。
定義環(huán)繞通知,在環(huán)繞通知中,先通過(guò)上述五種思路中的任意一種,對(duì)方法執(zhí)行的冪等性進(jìn)行判斷,判斷通過(guò)了,再執(zhí)行目標(biāo)方法,判斷不通過(guò),則直接拋出異常,不執(zhí)行目標(biāo)方法。
這就是自定義注解+AOP 的一個(gè)典型應(yīng)用場(chǎng)景。
2. 接口限流
對(duì)于接口限流,目前來(lái)說(shuō),一個(gè)比較成熟的方案是使用 Alibaba 的 Sentienl,簡(jiǎn)單配置一下就可以實(shí)現(xiàn)接口限流了。
但是如果沒(méi)有用這個(gè)工具呢?如果是我們自己寫(xiě)呢?毫無(wú)疑問(wèn),還是自定義注解+AOP,思路大致如下:
- 自定義注解。
- 在需要進(jìn)行限流的接口方法上添加自定義注解,同時(shí)還可以設(shè)置一些限流的參數(shù),例如時(shí)間窗口值、流量大小等。
- 自定義切點(diǎn),攔截規(guī)則就是所有添加了自定義注解的方法,攔截到方法之后,在環(huán)繞通知中,可以通過(guò) Redis 插件 redis-cell、通過(guò)漏斗算法去處理限流,這個(gè)我這里就不羅嗦了,之前的文章中都寫(xiě)過(guò)了。限流計(jì)算沒(méi)問(wèn)題的話,就執(zhí)行目標(biāo)方法,否則將操作攔截下來(lái)。
大致思路如上,說(shuō)白了就是自定義注解+ AOP,道理雖然簡(jiǎn)單,但是真正做起來(lái),還是有很多細(xì)節(jié)。
3. 日志處理
說(shuō)到 AOP,所有人都能想到的使用場(chǎng)景了,這個(gè)我就不羅嗦了,松哥之前也有過(guò)專門的文章介紹,沒(méi)看過(guò)的小伙伴們戳這里:記錄項(xiàng)目日志,一個(gè)注解搞定。
4. 多數(shù)據(jù)源處理
有時(shí)候我們項(xiàng)目中存在多個(gè)不同的數(shù)據(jù)源,在實(shí)際使用中需要進(jìn)行切換,網(wǎng)上也有一些開(kāi)源的解決方案,不過(guò)這個(gè)東西其實(shí)并不難,我們也可以自己寫(xiě)。
自定義多數(shù)據(jù)源的處理,大致上思路如下:
從 Spring2.0.1 中引入了 AbstractRoutingDataSource 類,(注意是 Spring2.0.1 不是 Spring Boot2.0.1,所以這其實(shí)也算是 Spring 一個(gè)非常古老的特性了), 該類充當(dāng)了 DataSource 的路由中介,它能夠在運(yùn)行時(shí), 根據(jù)某種 key 值來(lái)動(dòng)態(tài)切換到真正的 DataSource 上。
大致的用法就是你提前準(zhǔn)備好各種數(shù)據(jù)源,存入到一個(gè) Map 中,Map 的 key 就是這個(gè)數(shù)據(jù)源的名字,Map 的 value 就是這個(gè)具體的數(shù)據(jù)源,然后再把這個(gè) Map 配置到 AbstractRoutingDataSource 中,最后,每次執(zhí)行數(shù)據(jù)庫(kù)查詢的時(shí)候,拿一個(gè) key 出來(lái),AbstractRoutingDataSource 會(huì)找到具體的數(shù)據(jù)源去執(zhí)行這次數(shù)據(jù)庫(kù)操作。
基于以上知識(shí),我們可以自定義一個(gè)注解,在需要切換數(shù)據(jù)源的方法上,添加這個(gè)注解,然后通過(guò) AOP 去解析這個(gè)自定義注解,當(dāng)目標(biāo)方法被攔截下來(lái)的時(shí)候,我們跟進(jìn)注解中的配置,重新設(shè)置要執(zhí)行的數(shù)據(jù)源,這樣將來(lái) service 中的方法在執(zhí)行的過(guò)程中,就會(huì)使用到切換之后的數(shù)據(jù)源了。
5. 方法權(quán)限處理
這個(gè)其實(shí)也跟前面的差不多。
方法級(jí)別的權(quán)限處理,一般來(lái)說(shuō)也是基于注解來(lái)完成的。如果你使用了 Spring Security 之類的權(quán)限框架,就不用自己解析權(quán)限注解了,按照框架的要求直接來(lái)使用就行了。
有的時(shí)候,我們可能沒(méi)有使用 Spring Security,想自己處理權(quán)限注解,也是可以的。用戶自定義權(quán)限注解,為注解添加屬性,然后將注解添加到目標(biāo)方法上,再通過(guò) AOP 去解析這個(gè)注解,AOP 將目標(biāo)方法的執(zhí)行攔截下來(lái),然后判斷用戶是否具備所需要的權(quán)限,如果具備,就執(zhí)行目標(biāo)方法,否則就不執(zhí)行。
前兩天松哥剛剛分享的在微服務(wù)中,服務(wù)內(nèi)部的權(quán)限校驗(yàn),就是自定義一個(gè)注解,將從其他微服務(wù)上來(lái)的請(qǐng)求給攔截下來(lái),然后判斷請(qǐng)求的來(lái)源,如果是從其他微服務(wù)上來(lái)的,就執(zhí)行目標(biāo)方法,如果不是從其他微服務(wù)上來(lái)的,而是從外部來(lái)的請(qǐng)求,那么就將之?dāng)r截下來(lái)拋出異常,不執(zhí)行目標(biāo)方法。
6. 事務(wù)處理
這個(gè)倒是不需要自定義注解,對(duì)于聲明式事務(wù),直接用現(xiàn)成的注解就行了,但是本質(zhì)上也是 AOP,如果有小伙伴在 Spring 的 XML 中配置過(guò)事務(wù)的話,就知道這個(gè)東西底層也是 AOP。
好啦,梳理了幾個(gè)簡(jiǎn)單的案例,希望小伙伴們了解到 AOP 并不是屠龍術(shù),而是在日常開(kāi)發(fā)中有著廣泛應(yīng)用的技術(shù)。

























