配置如何優化
- 上文整合的過程中的還順帶整合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})
-
問題迎刃而解了,簡單不,驚喜不,不好,又奔騰了。。。。
-
啥話也不說,點個推薦吧!!!