SpringBoot整合多數據源的巨坑!!!


配置如何優化

  • 上文整合的過程中的還順帶整合Mybatis和TransactionManager,為什么還要重新定義他們呢?SpringBoot不是給我們都配置好了嗎?注意,此處優化就是這兩個配置去掉,直接用SpringBoot的自動配置,頓時高級了,別人一看你的代碼如此簡單就實現了多數據源的切換,牛叉不?
  • 如何去掉?SpringBoot萬變不離自動配置類,且看MybatisAutoConfiguration,如下:
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration implements InitializingBean {
  • 不多帖了,都是廢話,看前幾行就行了,醒目的一行啊,@ConditionalOnSingleCandidate(DataSource.class),什么鬼?該注解的意思就是IOC容器中只有一個指定的候選對象才起作用,但是我們注入了幾個DataSource,足足三個啊,這還起作用嗎?那不廢話嘛。
  • 事務管理器也是一樣,且看DataSourceTransactionManagerAutoConfiguration,如下:
public class DataSourceTransactionManagerAutoConfiguration {

	@Configuration
	@ConditionalOnSingleCandidate(DataSource.class)
	static class DataSourceTransactionManagerConfiguration {
  • 又看到了什么,@ConditionalOnSingleCandidate(DataSource.class)同樣的醒目,mmp,這不玩我呢嗎。這怎么搞?
  • 咦,不着急,此時就要看看@ConditionalOnSingleCandidate注解搞了什么,進去看看,有如下的介紹:
The condition will also match if multiple matching bean instances are already contained in the BeanFactory but a primary candidate has been defined; essentially, the condition match if auto-wiring a bean with the defined type will succeed.
  • 什么鬼,看不懂,英語太差了吧,不着急,陳某給大家推薦一個IDEA插件,文檔翻譯更加專注於程序員的專業術語,不像xx度翻譯,如下:

  • 好了,翻譯准確了就知道了,大致意思就是IOC容器中允許你有多個候選對象,但是你必須有一個主(primary)候選對象,頓時靈光一現,這不就是@Primary注解嗎,艹,我這也太優秀了吧。
  • 二話不說,直接開擼,輕輕松松一個注解搞定,此時的數據源配置變得簡單多了,如下:
/**
 * @Description 數據源的配置
 * @Author CJB
 * @Date 2020/3/9 13:45
 */
@Configuration
@MapperScan(basePackages = {"com.vivachek.service.dao","com.vivachek.service.dao2"})
public class DatasourceConfig {

    /**
     * 注入數據源1
     */
    @ConfigurationProperties(prefix = "spring.datasource1")
    @Bean(value = "dataSource1")
    public DataSource dataSource1() {
        return new DruidDataSource();
    }

    /**
     * 第二個數據源
     */
    @Bean(name = "dataSource2")
    @ConfigurationProperties(prefix = "spring.datasource2")
    public DataSource dataSource2() {
        return new DruidDataSource();
    }

    /**
     * 動態數據源
     *
     * @return
     */
    @Bean
    @Primary
    public DynamicDataSource dynamicDataSource() {
        DynamicDataSource dataSource = new DynamicDataSource();
        //默認數據源,在沒有切換數據源的時候使用該數據源
        dataSource.setDefaultTargetDataSource(dataSource2());
        HashMap<Object, Object> map = Maps.newHashMap();
        map.put("dataSource1", dataSource1());
        map.put("dataSource2", dataSource2());
        //設置數據源Map,動態切換就是根據key從map中獲取
        dataSource.setTargetDataSources(map);
        return dataSource;
    }
}
  • 直接在DynamicDataSource添加了一個@Primary就省去了SqlSessionFactory和TransactionManager的手動配置,是不是很easy並且顯得自己很牛叉,太有成就感了.....
  • 好了,牛也吹了,運行一下吧,滿懷期待等待30秒.......,what?什么鬼?失敗了,拋出了異常,如下:

  • 什么鬼,循環依賴異常,搞什么飛機,一萬個草泥馬在奔騰在橫無際涯的草原上。。。。。。。。
  • 別急,還有后續,關注我,將會定時更新后續文章。另外需要源碼的聯系我,微信聯系方式在個人獨立博客【關於我】中,加我注明來意,謝謝。
  • 別忘了點贊喲,多來走動走動唄..........

動態路由數據源添加@Primary報循環依賴異常

  • 前面文章Spring解決循環依賴有說過Spring對於循環依賴是完全能夠解決的,沒有讀過的小伙伴建議看一下,里面詳細的講述了Spring是如何解決循環依賴的,此處就不再贅述了。
  • 既然Spring能夠解決循環依賴,為什么這里又會報循環依賴的異常呢?我們不妨跟着代碼看看是怎樣的循環依賴,如下:

  • 上面兩個數據源都是自己定義的,先不用看,那么肯定是DataSourceInitializerInvoker造成的循環依賴了,果不其然,其中確實依賴了DataSource,源碼如下:
DataSourceInitializerInvoker(ObjectProvider<DataSource> dataSource, DataSourceProperties properties,
			ApplicationContext applicationContext) {
		this.dataSource = dataSource;
		this.properties = properties;
		this.applicationContext = applicationContext;
	}

  • what?即使依賴了又怎樣?Spring不是可以解決循環依賴嗎?別着急下面分析

  • ObjectProvider應該不陌生吧,其實內部就是從IOC容器中獲取Bean而已,但是,轉折來了......... ,這是什么,這是構造器,Spring能解決構造器的循環依賴嗎?答案是不能,所以原因找到了,這里不再細說了,欲知原因請讀Spring解循環依賴

  • 問題找到了,如何解決?此時心中一萬個草泥馬奔騰,怎么解決呢?

  • 哈哈,此時插播一條廣告,本人的獨立博客已經發布了很多文章,感興趣的可以收藏一下,【關於我】中有我的微信聯系方式,歡迎交流。

  • 回到正題,如何解決?很簡單,找到這個DataSourceInitializerInvoker是什么時候注入到IOC容器中的,因此我們找到了DataSourceAutoConfiguration,繼而找到了DataSourceInitializationConfiguration這個配置類,源碼如下:

@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
    @Configuration
	@Conditional(EmbeddedDatabaseCondition.class)
	@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
	@Import(EmbeddedDataSourceConfiguration.class)
	protected static class EmbeddedDatabaseConfiguration {

	}

	@Configuration
	@Conditional(PooledDataSourceCondition.class)
	@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
	@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
			DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.Generic.class,
			DataSourceJmxConfiguration.class })
	protected static class PooledDataSourceConfiguration {

	}   
}
    
    
@Configuration
@Import({ DataSourceInitializerInvoker.class, DataSourceInitializationConfiguration.Registrar.class })
class DataSourceInitializationConfiguration {
  • 貼了那么多代碼誰看的懂?草泥馬又奔騰了,可以看到源碼中出現了兩次@ConditionalOnMissingBean({ DataSource.class, XADataSource.class }),這什么鬼,不多說了,相信讀過SpringBoot源碼的都知道,這個配置類根本不起作用啊,那還要它干嘛,直接搞掉不就完事了。好了,分析到這里終於知道解決的方案了,搞掉DataSourceAutoConfiguration,怎么搞呢?一個注解搞定。
//排除配置類
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
  • 問題迎刃而解了,簡單不,驚喜不,不好,又奔騰了。。。。

  • 啥話也不說,點個推薦吧!!!

微信搜索碼猿技術專欄


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM