花了2個鐘才搞懂這AOP爲啥沒生效,水友卻睡着了……

「本文已參與好文召集令活動,點擊查看:後端、大前端雙賽道投稿,2萬元獎池等你挑戰!前端

SpringbootAOP 失效的緣由

今天 4ye 來和小夥伴們分享一個小實戰啦 ,沖沖衝~ (。・∀・)ノjava

img

實戰回顧

(我竟然拖到如今才寫了這文章…… 😵)git

主要是在一個週日(2020.6.6)在技術羣裏看到一個老哥在問github

「怎麼用切面來捕獲自定義異常?」 ( ̄▽ ̄)"web

我當時想的是,捕獲異常不是很常見的嗎,平時常常用到這個全局異常捕獲 ,因而就把本身 GitHub 上的小例子發給他(主要是這個 ControllerAdvice 註解),如圖 👇spring

image-20210612084149929

結果老哥過了一段時候就加了我,還問我有沒有空幫忙看下,還想打電話問我,我當時的心裏是後端

這麼急的嗎(瑟瑟發抖……)springboot

img

不過恰好在外面沒時間,就婉拒了,後來晚上回來,愣是花了兩個鍾,纔看懂 爲啥這個AOP 沒有生效?爲啥沒能捕獲到異常?markdown

img

下面進入主題👉(項目總體介紹maven

maven項目結構圖

在該項目中,maven項目採用多模塊構建父子結構,由 父maven 來統一管理這些公共包,以及項目的總體版本屬性配置等

裏面有兩個模塊,starter 模塊和 core 模塊,其中 starter 模塊依賴 core 模塊,總體以下圖👇

image-20210612180832771

子模塊介紹

不知道小夥伴們看到上面的 starter 有沒有嗅到什麼 ?

一開始我覺得是常見的 自定義starter ,可是裏面的內容卻和我想的有點出入,竟然只有一個 spring.factories 文件, 很明顯這裏使用到了 Springboot 的 SPI 機制

這個在咱們以前的 👉 Springboot自動裝配原理探索 一文中有介紹到,小夥伴們能夠前去了解看看~😄

對兩個模塊中的核心部分進行展開,結構以下👇

image-20210612182553858

core項目描述非web項目裏面只有 service ,沒有啓動類等

👉 因爲 core 模塊 不是web項目!!,因此這個 ControllerAdvice 是確定不能用的,畢竟它是在 web 包中的,通常咱們在 web項目中配合這個@ExceptionHandler(Exception.class) 實現全局異常捕獲,而後進行統一處理的。

👉 從 Springboot 的 SPI 機制 中咱們能夠得知,Springboot 項目啓動時,會去掃描各個項目中的 META-INF/spring.factories 文件(包括各個jar包),而後將其中的配置信息讀取到內存中,而自動配置時會根據必定的條件對這些類進行篩選,最後建立符合的類,完成這個自動裝配。

很明顯,這裏就是經過自動配置,來實現相關 bean 的注入。

配置類說明

那麼,在瞭解了這些基本信息後,咱們能夠把目光移到這個 xxxConfig 上,這裏模仿了一個👇

image-20210706062207407

緊接着就是項目中的切面配置了,例如之前寫的小例子:👇

代碼在個人 GitHub

github.com/RyzeYang/sp…

經過異常通知來捕獲

image-20210707075730630

除了上面這兩個以外,項目中沒有用到其餘配置了!

問題來了

這個時候問題就來了,在定義了切面以後,發現根本沒有在項目中起做用!而其餘均可以正常運行!

img

因而我一直在想,這是爲啥呀,明明切面已經定義好了呀……

img

終於,我開始了嘗試,在 yaml 配置文件中添加這個參數

spring:
  aop:
    auto: true
複製代碼

由於在印象中,這個默認是 true ,會默認使用這個 @EnableAspectJAutoProxy , 不用咱們手動去添加這個 @EnableAspectJAutoProxy 註解(以前一直沒有手動添加這個註解)🐖

image-20210706070907862

結果也沒什麼效果……

因而乎,我決定手動添加到剛剛那個 xxxConfig 配置類上,結果也沒有什麼做用……

image-20210706070415418

終於,我纔想起那個 切面配置 沒有被加載到這個 Spring 中 ,因而我又在那個配置類 xxxConfig 上添加了這個包掃描註解 @ComponentScan(basePackages = "com.xxx.xxx")

結果終於成功了!

因而我趕在 23:59 將修改後的文件發給那位老哥後,卻發現他竟然睡着了 哈哈哈

image-20210706072851025

img

問題剖析

解決問題後,咱們能夠發現這個問題就下面兩點👇

  1. 沒有將切面註冊到 SpringIOC 容器中
  2. 沒有使用這個 @EnableAspectJAutoProxy

第一步的解決也很簡單,就是沒有配置這個包掃描 @ComponentScan(basePackages = "com.xxx.xxx")

第二步的解決嘛,就有點一頭霧水了當時,畢竟以前也不須要我手動去添加的,並且從配置的描述信息中能夠發現,即便咱們沒有配置,他也是默認開啓的,會自動使用這個註解的~

那麼小夥伴們知道第二步問題的所在嗎😄

img

嘿嘿,答案就出在這個自動配置 身上,能夠發現咱們上面都沒有使用到這個 @EnableAutoConfiguration 註解,而在咱們的 SpringBootApplication 組合註解中,最重要的就是它了! 經過它去開啓了咱們的這個自動裝配

這個時候又得把這文章搬出來了 👉 Springboot自動裝配原理探索 哈哈

那麼咱們再來看看這個 AOP自動裝配的配置類

AOP自動裝配的配置類

AopAutoConfiguration 源碼以下👇

AopAutoConfiguration

開頭有這麼一個條件註解

@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
複製代碼

havingValue = "true" 的意思是:值爲 true 時纔有效

matchIfMissing = true 的意思是:沒有配置這個屬性時也能加載

接着讓咱們把目光移到第一個靜態內部類: AspectJAutoProxyingConfiguration

如圖👉:

image-20210706083310587

這個咱們也比較熟悉啦, proxyTargetClasstrue 時表示使用 cglibfalse 使用 JDK動態代理

接着看最後的靜態內部類: ClassProxyingConfiguration

image-20210706083542395

能夠發現它這裏的條件是和 AspectJAutoProxyingConfiguration 相反的,當沒有這個 Advice 類時,幫咱們去註冊這個代理到 IOC

看完該AOP自動裝配類後, 咱們能夠發現當咱們使用 @EnableAutoConfiguration 自動裝配註解時並引入 AOP 的包時,它會自動幫咱們裝配這個AopAutoConfiguration ,而它裏面就使用到了 @EnableAspectJAutoProxy ,因此咱們通常不用手動添加該註解。

img

怎麼測試

嘿嘿,再出一個小問題考考小夥伴😝

有沒有細心的小夥伴發現上面的 core 模塊是沒有用到這個 SpringBootApplication 的,並且咱們也沒有用到這個 @EnableAutoConfiguration ,那麼沒有自動裝配,這個 SPI 顯然也沒啥做用 ,那麼,咱們在項目中要怎麼測試呢~

img

答案就在 單元測試的註解身上 @SpringBootTest(classes = xxxConfiguration.class)

經過這個 classes ,咱們直接指定並實例化這個配置類就能夠了

我也是報錯了才知道 哈哈😝

image-20210707072044403

總結

嘿嘿,老規矩,畫個圖總結下啦👇

排查思路

image-20210707081614802

知識腦圖

image-20210707084227045

那麼,本期就分享到這啦,喜歡的小夥伴記得點點贊呀~ 下期看看狀況分享下下面某一個叭😝

  • Springboot自定義starter
  • 利用AOP實現插件
  • Spring源碼

img

最後

歡迎小夥伴們來一塊兒探討問題~

若是你以爲本篇文章還不錯的話,那拜託再點點贊支持一下呀😝

讓咱們開始這一場意外的相遇吧!~

歡迎留言!謝謝支持!ヾ(≧▽≦*)o 沖沖衝!!

我是4ye 我們下期應該……很快再見!! 😆

相關文章
相關標籤/搜索