問題:
使用springboot進行多數據源時,發生了單例DataSource對應多個DataSourceBean的問題。
具體錯誤如下:XXXXX required a single bean, but 3 were found。通過@Qualifier來區分,或是在@Bean中添加name屬性來區分,都沒有作用。
問題的根本原因:
主要在於SpringBoot的DataSourceInitializer,該類在autoConfigure包中,用來自動初始化一個內置的DataSource實例,在創建該實例的時候發生了注入的問題。
創建實例的流程(記錄一下方便以後再次調試):
1. 調用DataSourceInitializer的構造方法
2. 調用AbstractAutowireCapableBeanFactory的applyMergedBeanDefinitionPostProcessors方法
3. 調用AbstractAutowireCapableBeanFactory的initializeBean方法
4. AbstractAutowireCapableBeanFactory的applyBeanPostProcessorsBeforeInitialization方法,其中有一個循環是用多種Bean處理器來處理DataSourceInitializer對象
5. 之後使用反射的方式跳轉到DataSourceInitializer的init方法
6.裏面通過this.applicationContext.getBean(DataSource.class)來獲取所有DataSource的實現類對象實例。
7. DefaultListableBeanFactory的resolveNamedBean方法中來選取實例對象,通過裏面的getBeanNamesForType方法獲取到所有的符合requireType(也就是DataSource.class)的對象。
8. 如果對象實例的實例數量大於1,則會進入以下的兩個判斷:
判斷是否有Primary的實例,或者是優先級高的實例對象,如果有,則將候選對象名賦值給candidateName。沒有則置爲空,最後拋出多個實例的異常。
其中問題出在:
因爲項目中只使用了@Qualifier,而且springboot的DataSourceInitializer沒有對@Qualifier的處理,所以沒有對實例進行匹配。
造成多個數據源實例。對於存在多數據源的情況,他們做的補救措施是在代碼中添加了是否是Primary和是否是HighestPriority的判斷,
來處理採用哪個數據源。所以解決的方式也因此出來了。就是將某個實例標記爲Primary或者HighestPriority。
解決問題的方法:
在某個@Bean上添加@Primary註解,就可以做到唯一區分。
這裏其實應該還可以通過優先級來區分,但使用@Order發現並不是這個優先級,也沒找到相關的資料,所以之後再研究一下。