Spring Interceptor 自動注入導致迴圈依賴
1,bug現場還原
迴圈依賴
1.1,在攔截器配置類,通過構造器方式,依賴攔截器,重寫addInterceptors函式把攔截器注入到攔截器鏈中。
1.2,而攔截器中也是通過構造器方式,依賴一個thirdAuthClient
1.3,thirdAuthClient(一個feign客戶端介面)
專案啟動報錯,顯示這三個類迴圈依賴
2,bug分析
2.1假說
看報錯,前兩個依賴能理解,但是ThirdAuthClient依賴ThirdInterceptorConfig無法理解,這只是一個@FeignClient,應該是在spring啟動之後載入。看類圖關係也看不出結果,初步懷疑是跟spring bean載入順序有關係,雖然看到的是迴圈依賴,但實際可能是用錯了載入方式。
2.2梳理
首先關注Spring MVC Interceptor的注入方式
這是官方檔案中攔截器的註冊方式,直接new出來的。那麼關注點就變成了:
為什麼Interceptor不可以通過@Autowired方式或者構造器進行注入?
搜了一下,看到這樣的說法:
1,SpringBoot專案的Bean裝配預設規則是根據Application類所在的包( com.boot.app )位置從上往下掃描
2, 攔截器執行在自動bean初始化之前
這個說法和假說一致,繼續思考:攔截器是否確實執行在自動bean初始化之前
在我的使用場景裡,我覺得正確的順序應該是
攔截器的所有依賴(比如ThirdAuthClient)被初始化bean並注入Spring容器
執行攔截器注入
初始化攔截器bean物件,此時需要1中已經存在的bean物件作為攔截器的依賴項
攔截器注入成功
如果這樣想,那麼說法2和這個想法不一致,或者說,官方檔案中定義的攔截器是常規自定義,不需要依賴,直接new就行了,而我自定義的攔截器通過構造器,顯示地宣告瞭依賴,可能是這種區別導致了迴圈依賴。
又找了一下構造器的其他注入方式,如下
可以通過@Bean注入的方式,那麼造成攔截器迴圈依賴的ThirdAuthClient通過@Autowired注入,其他類通過構造器注入
這樣在執行我想法中的順序2的時候,順序1所需要的bean就準備好了。
3,分析
首先根據Spring載入及例項化Bean的順序,如下
迴圈引用報錯應該是Spring在遞迴解析的時候檢測出來的,並且跟我寫的構造器方式初始化bean有關係。在網上看到的攔截器的注入方式基本都是手動建立的。
那麼這裡明確的疑問是:
1,攔截器是否只能手動建立(new出來)?不可以讓Spring去管理物件並建立嗎?
分析:這個載入過程涉及到了@ConfigurationProperties
,@Component
,@FeignClient
,@Configuration
,@Component
這些註解
@ConfigurationProperties
註解屬於Spring配置類註解,最先被載入
@FeignClient
註解,由FeignClientsRegistrar
類實現了ImportBeanDefinitionRegistrar
,通過registerBeanDefinitions
方法向容器中注入 @FeignClient
註解的介面
@Configuration
,@Component
,屬於配置類,Spring自動載入
根據Spring載入Bean的邏輯,這些註解都在Spring的可見範圍內,是可以通過遞迴解析來分析這些例項的依賴關係,並在合適的時機例項化。
這麼分析的話,問題可能是出在構造器上,構造器的顯示宣告,替代了Spring在解析這些例項的時候所依據的遞迴解析方式,造成了這些例項不能被依賴解析所解決,如果不使用構造器,那麼我覺得這些註解所宣告的類是可以放在Spring的依賴解析來處理的。
4,實現
1,手動建立攔截器例項實現
攔截器不加任何自動配置註解,防止Spring自動裝配和初始化,並通過構造器顯示宣告依賴
攔截器配置類通過構造器和@Autowired方式,準備好攔截器需要的bean
執行順序3的時候,通過@Bean方式手動建立攔截器例項,把攔截器的初始化交給Spring。
這是沒更改構造器方式去注入的,實踐可行。
2,讓Spring容器管理攔截器建立
這是去除了構造器,用註解來替代手動建立攔截器例項,實踐可行。
5,結論
攔截器建立可以不通過手動建立,而是交給Spring的容器管理來管理。
參考檔案
stackoverflow.com/questions/2…