關(guān)于@DS注解切換數(shù)據(jù)源失敗的原因?qū)崙?zhàn)記錄
項(xiàng)目場(chǎng)景:
一位好友在陳年老代碼中想要加入mybatis-plus和boumidou的多數(shù)據(jù)源來(lái)輕松實(shí)現(xiàn)crud,但是發(fā)現(xiàn)@DS無(wú)法成功切換數(shù)據(jù)源,一直都是訪問(wèn)的主庫(kù),于是我開(kāi)始了漫長(zhǎng)的啃源碼找原因。
以下內(nèi)容包含兩個(gè)問(wèn)題1:配置的數(shù)據(jù)庫(kù)沒(méi)有正常被dynamic框架載入,2:@DS注解切換數(shù)據(jù)源無(wú)效
問(wèn)題排查過(guò)程
第一個(gè)問(wèn)題,數(shù)據(jù)源沒(méi)有載入
根據(jù)啟動(dòng)日志里druid打印的信息來(lái)看,只載入了一個(gè)數(shù)據(jù)庫(kù),并沒(méi)有把yml配置信息中的兩個(gè)數(shù)據(jù)庫(kù)都加載,YML配置如下。
從源碼來(lái)看,數(shù)據(jù)源的配置讀取是在AbstractDataSourceProvider類(lèi)中。
他的調(diào)用是在子類(lèi)YmlDynamicDataSourceProvider中。
然后在DynamicDataSourceAutoConfiguration類(lèi)中注入了YmlDynamicDataSourceProvider。
在此處打上斷點(diǎn)后,發(fā)現(xiàn)傳入的datasourceMap中的信息都是正確的,跟yml配置的一致。
那就只能是YmlDynamicDataSourceProvider中的loadDataSources()方法沒(méi)有調(diào)用。
此方法在DynamicRoutingDataSource的初始化方法中調(diào)用。
在這里打上斷點(diǎn)后發(fā)現(xiàn)斷點(diǎn)進(jìn)不來(lái),是對(duì)象沒(méi)有創(chuàng)建的原因,回到DynamicDataSourceAutoConfiguration類(lèi)中找DynamicRoutingDataSource的創(chuàng)建。
@ConditionalOnMissingBean表明dataSource這個(gè)Bean應(yīng)該是已經(jīng)被注入了,通過(guò)全局查詢找到了問(wèn)題的關(guān)鍵點(diǎn)(下圖),代碼在其他地方注入了dataSource。
然而很多地方已經(jīng)在用這個(gè)dataSource了,刪了的話,就雪崩了。考慮另辟蹊徑,選擇了自己去創(chuàng)建DynamicRoutingDataSource對(duì)象。
到這里,成功載入了其他數(shù)據(jù)源。
第二個(gè)問(wèn)題,@DS注解切換數(shù)據(jù)源無(wú)效
先從@DS的攔截盤(pán)起,@DS的攔截在DynamicDataSourceAnnotationInterceptor攔截器中,讀取了@DS上的庫(kù)名,然后放入一個(gè)上下文管理器的隊(duì)列中。
調(diào)用SQL時(shí),會(huì)通過(guò)AbstractRoutingDataSource的getConnection()方法(下圖)。
里面的determineDataSource()方法由子類(lèi)DynamicRoutingDataSource實(shí)現(xiàn),真正切換了數(shù)據(jù)源,里面調(diào)用了隊(duì)列的peek()方法,取出上面攔截器中加入的@DS屬性的庫(kù)名,然后通過(guò)庫(kù)名,拿到了對(duì)應(yīng)數(shù)據(jù)源的DataSource,再結(jié)合AbstractRoutingDataSource的getConnection()方法拿到了對(duì)應(yīng)數(shù)據(jù)庫(kù)的連接,去執(zhí)行SQL。
切換數(shù)據(jù)源的流程到這就說(shuō)的差不多了,然后講講這個(gè)項(xiàng)目中為什么切換不了數(shù)據(jù)源。
在determineDataSource()方法打上斷點(diǎn),結(jié)果根本沒(méi)有經(jīng)過(guò)該方法,然而@DS的攔截器斷點(diǎn)是能正常進(jìn)入的,@DS注解上的數(shù)據(jù)源名稱也讀取正確,是第二個(gè)庫(kù)。由此猜測(cè),應(yīng)該是沒(méi)有走進(jìn)AbstractRoutingDataSource的getConnection()方法,查看druid的數(shù)據(jù)庫(kù)加載相關(guān)的日志,發(fā)現(xiàn)在boumidou的dynamic框架加載數(shù)據(jù)庫(kù)之前,已經(jīng)有其他數(shù)據(jù)庫(kù)加載了。
推測(cè)可能是由于,SQL調(diào)用的時(shí)候,默認(rèn)是用的第一個(gè)數(shù)據(jù)庫(kù)的DataSource調(diào)用的getConnection()方法,但是這個(gè)不是由dynamic框架加載,并不是AbstractRoutingDataSource對(duì)象,所以不會(huì)走進(jìn)determineDataSource()方法去選擇數(shù)據(jù)源,就試著想辦法讓dynamic框架優(yōu)先加載。
在上面自己創(chuàng)建DynamicRoutingDataSource的@Bean方法上,加上了@Primary注解,去掉了引起問(wèn)題一的方法上的@Primary注解,來(lái)讓多數(shù)據(jù)源優(yōu)先加載。
然后就成功了,也能通過(guò)@DS正常切換數(shù)據(jù)源了。
總結(jié)
到此這篇關(guān)于關(guān)于@DS注解切換數(shù)據(jù)源失敗的原因的文章就介紹到這了,更多相關(guān)@DS注解切換數(shù)據(jù)源失敗內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring?aop?pointcut?添加多個(gè)execution方式
這篇文章主要介紹了spring?aop?pointcut?添加多個(gè)execution方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11Java并發(fā)編程中的CyclicBarrier使用解析
這篇文章主要介紹了Java并發(fā)編程中的CyclicBarrier使用解析,CyclicBarrier從字面意思上來(lái)看,循環(huán)柵欄,這篇文章就來(lái)分析下是到底是如何實(shí)現(xiàn)循環(huán)和柵欄的,需要的朋友可以參考下2023-12-12MyBatis的<foreach>以及java代碼的批處理方式
這篇文章主要介紹了MyBatis的<foreach>以及java代碼的批處理方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08Java中token的存儲(chǔ)和獲取實(shí)例代碼
關(guān)于java獲取微信Token驗(yàn)證的問(wèn)題相信很多人都遇見(jiàn)過(guò),尤其是對(duì)剛接觸微信開(kāi)發(fā)的人來(lái)說(shuō)確實(shí)有點(diǎn)棘手,下面這篇文章主要給大家介紹了關(guān)于Java中token存儲(chǔ)和獲取的相關(guān)資料,需要的朋友可以參考下2022-08-08Java實(shí)現(xiàn)企業(yè)員工管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)企業(yè)員工管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02java中List去除重復(fù)數(shù)據(jù)的5種方式總結(jié)
這篇文章主要給大家總結(jié)介紹了關(guān)于java中List去除重復(fù)數(shù)據(jù)的5種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01Java實(shí)現(xiàn)的二叉樹(shù)常用操作【前序建樹(shù),前中后遞歸非遞歸遍歷及層序遍歷】
這篇文章主要介紹了Java實(shí)現(xiàn)的二叉樹(shù)常用操作,包括二叉樹(shù)的前序建樹(shù),前中后遞歸非遞歸遍歷及層序遍歷等相關(guān)操作技巧,需要的朋友可以參考下2018-01-01