1. 程式人生 > 程式設計 >Spring Interceptor 自動注入導致迴圈依賴

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初始化之前

在我的使用場景裡,我覺得正確的順序應該是

  1. 攔截器的所有依賴(比如ThirdAuthClient)被初始化bean並注入Spring容器

  2. 執行攔截器注入

  3. 初始化攔截器bean物件,此時需要1中已經存在的bean物件作為攔截器的依賴項

  4. 攔截器注入成功

如果這樣想,那麼說法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的容器管理來管理。

參考檔案

www.cnblogs.com/shamo89/p/8…

stackoverflow.com/questions/2…