一文搞懂MyBatis多數(shù)據(jù)源Starter實現(xiàn)
前言
本文將實現(xiàn)一個MyBatis的Springboot的Starter包,引用這個Starter包后,僅需要提供少量配置信息,就能夠完成MyBatis多數(shù)據(jù)源的初始化和使用,相較于MyBatis官方的Starter包,擴(kuò)展了多數(shù)據(jù)源的使用場景。
本文的所有源碼可以從如下倉庫下載。
Springboot版本:2.7.6
正文
一. 實現(xiàn)思路
要實現(xiàn)Starter包,肯定需要借助Springboot的自動裝配機(jī)制,所以我們首先需要提供自動裝配的配置類。
然后我們需要加載多個數(shù)據(jù)源的配置并且生成對應(yīng)的數(shù)據(jù)源,同時還需要可以根據(jù)用戶配置的type創(chuàng)建不同的數(shù)據(jù)源,例如可以支持創(chuàng)建HikariCP,Druid和TomcatJdbc的數(shù)據(jù)源。
創(chuàng)建出來的數(shù)據(jù)源需要根據(jù)用戶的配置,設(shè)置給不同的SqlSessionFactory,然后不同的SqlSessionFactory設(shè)置給不同的MapperScannerConfigurer,最終實現(xiàn)的效果就是一部分映射接口使用一個數(shù)據(jù)源,另一部分映射接口使用另一個數(shù)據(jù)源。
最后,還需要提供一種手段,抑制Springboot原生的數(shù)據(jù)源加載,這個功能我們可以通過ApplicationContextInitializer這個擴(kuò)展點來完成。
整體的一個思維導(dǎo)圖如下所示。

二. 自動裝配實現(xiàn)
由于適配的是Springboot的2.7.x版本,所以需要在resources\META-INF\spring目錄下創(chuàng)建org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,且內(nèi)容如下所示。
com.lee.multidatasource.autoconfig.LeeMultiPersistenceAutoConfiguration
上述的LeeMultiPersistenceAutoConfiguration就是完成自動裝配的配置類,實現(xiàn)如下。
@AutoConfiguration
@Import({LeeMultiPersistenceConfiguration.class, DataSourceBeanPostProcessor.class,})
public class LeeMultiPersistenceAutoConfiguration {}通過LeeMultiPersistenceAutoConfiguration導(dǎo)入了兩個bean,一個是LeeMultiPersistenceConfiguration,用于加載配置以及創(chuàng)建數(shù)據(jù)源和MyBatis的bean,另一個是DataSourceBeanPostProcessor,用于對LeeMultiPersistenceConfiguration創(chuàng)建的bean做一些后置處理。
上述就是自動裝配的實現(xiàn),主要就是將LeeMultiPersistenceAutoConfiguration注冊到容器中,而LeeMultiPersistenceAutoConfiguration也是整個Starter包實現(xiàn)的關(guān)鍵。
三. 配置加載
約定數(shù)據(jù)源和MyBatis的配置需要遵循如下規(guī)則。
lee:
persistence:
dataSourceName1:
datasource:
type: ...
max-lifetime: ...
keep-alive-time: ...
driver-class-name: ...
url: ...
username: ...
password: ...
pool-name: ...
mybatis:
configLocation: ...
basePackage: ...
dataSourceName2:
datasource:
max-lifetime: ...
keep-alive-time: ...
driver-class-name: ...
url: ...
username: ...
password: ...
pool-name: ...
mybatis:
configLocation: ...
basePackage: ...在lee.persistence的下一級的配置,是數(shù)據(jù)源的名字,可以由用戶自定義,這個名字最終會作為數(shù)據(jù)源的bean在IOC容器中的名字。
在lee.persistence.dataSourceName的下一級的配置,固定是兩個配置項,其一是lee.persistence.dataSourceName.datasource,用于設(shè)置數(shù)據(jù)源相關(guān)的配置,其二是lee.persistence.dataSourceName.mybatis,用于設(shè)置MyBatis相關(guān)的配置。
由于數(shù)據(jù)源的名字和個數(shù)都可以由用戶自定義,那么很難基于@ConfigurationProperties注解來一步到位的完成上述數(shù)據(jù)源配置的加載,我們需要基于Environment來自行處理。
下面來看一下用于處理數(shù)據(jù)源配置的LeeMultiPersistenceConfiguration的類圖,如下所示。

LeeMultiPersistenceConfiguration首先實現(xiàn)了EnvironmentAware接口,從而可以拿到Environment對象,其次實現(xiàn)了ImportBeanDefinitionRegistrar接口,從而可以向Spring注冊BeanDefinition,那么實際上LeeMultiPersistenceConfiguration做的事情就是加載配置以及向Spring注冊數(shù)據(jù)源和MyBatis相關(guān)組件的BeanDefinition。下面看一下LeeMultiPersistenceConfiguration實現(xiàn)的registerBeanDefinitions() 方法。
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
// 加載并解析數(shù)據(jù)源配置
MultiPersistenceProperties multiPersistenceProperties = parseMultiPersistenceProperties();
List<String> persistenceNames = multiPersistenceProperties.getPersistenceNames();
// 為每個數(shù)據(jù)源注冊BeanDefinition
for (String persistenceName : persistenceNames) {
registerDatasource(registry, persistenceName, multiPersistenceProperties.getDataSourceProperties(persistenceName));
registerSqlSessionFactory(registry, persistenceName, multiPersistenceProperties.getMybatisProperties(persistenceName));
registerMapperScannerConfigurer(registry, persistenceName, multiPersistenceProperties.getMybatisProperties(persistenceName));
}
}本節(jié)就先分析一下數(shù)據(jù)源配置的加載和解析,具體就是LeeMultiPersistenceConfiguration的parseMultiPersistenceProperties() 方法,如下所示。
private MultiPersistenceProperties parseMultiPersistenceProperties() {
MultiPersistenceProperties multiPersistenceProperties = new MultiPersistenceProperties();
// 將數(shù)據(jù)源相關(guān)的配置加載為MultiPersistencePropertiesWrapper
MultiPersistencePropertiesWrapper multiPersistencePropertiesWrapper = parseMultiPersistencePropertiesWrapper();
List<String> persistenceNames = multiPersistencePropertiesWrapper.getPersistenceNames();
// 遍歷每一個數(shù)據(jù)源并拿到這個數(shù)據(jù)源下數(shù)據(jù)源相關(guān)配置和MyBatis相關(guān)配置
for (String persistenceName : persistenceNames) {
DataSourceProperties dataSourceProperties = multiPersistencePropertiesWrapper
.getPersistenceDataSourceProperties(persistenceName);
MybatisExtendProperties mybatisProperties = multiPersistencePropertiesWrapper
.getPersistenceMybatisProperties(persistenceName);
// 添加當(dāng)前數(shù)據(jù)源的配置信息到MultiPersistenceProperties中
multiPersistenceProperties.addPersistenceProperties(
persistenceName, dataSourceProperties, mybatisProperties);
}
return multiPersistenceProperties;
}
private MultiPersistencePropertiesWrapper parseMultiPersistencePropertiesWrapper() {
Map<String, Map<String, Map<String, String>>> persistenceProperties;
Binder binder = Binder.get(environment);
// PERSISTENCE_PREFIX為lee.persistence
persistenceProperties = binder.bind(PERSISTENCE_PREFIX, Bindable.of(Map.class)).get();
persistencePropertiesCache = persistenceProperties;
return new MultiPersistencePropertiesWrapper(persistenceProperties);
}上述方法解析數(shù)據(jù)源配置是在parseMultiPersistencePropertiesWrapper() 方法中,思路是先將我們的數(shù)據(jù)源配置基于Binder解析為Map的形式,因為lee.persistence這個配置前綴已經(jīng)是確定的,所以在Binder的bind() 方法中傳入的配置名就是lee.persistence,那么最終得到的配置的Map實際內(nèi)容如下所示。
// 數(shù)據(jù)源名就是上面示例的dataSourceName1 // 配置類型就是datasource或mybatis Map[數(shù)據(jù)源名, Map[配置類型, Map[配置key, 配置value]]]
通常上述這種層級很多的Map,無論是可讀性還是易用性都很差,所以我們可以使用一個包裝類來包裝一下這種Map,并進(jìn)行適當(dāng)充血,來方便對這種多層級Map的使用,這里的包裝類就是MultiPersistencePropertiesWrapper,如下所示。
public class MultiPersistencePropertiesWrapper {
private Map<String, Map<String, Map<String, String>>> multiPersistenceProperties;
public MultiPersistencePropertiesWrapper(Map<String, Map<String, Map<String, String>>> multiPersistenceProperties) {
this.multiPersistenceProperties = multiPersistenceProperties;
}
// 只對multiPersistenceProperties提供set方法
public void setMultiPersistenceProperties(Map<String, Map<String, Map<String, String>>> multiPersistenceProperties) {
this.multiPersistenceProperties = multiPersistenceProperties;
}
// 獲取數(shù)據(jù)源的個數(shù)
public int getPersistenceSize() {
return multiPersistenceProperties.size();
}
// 獲取所有數(shù)據(jù)源的名字
public List<String> getPersistenceNames() {
return new ArrayList<>(multiPersistenceProperties.keySet());
}
// 獲取某個數(shù)據(jù)源對應(yīng)的數(shù)據(jù)源的配置類DataSourceProperties
public DataSourceProperties getPersistenceDataSourceProperties(String persistenceName) {
DataSourceProperties dataSourceProperties = new DataSourceProperties();
Map<String, Map<String, String>> persistenceProperties = multiPersistenceProperties.get(persistenceName);
Map<String, String> persistenceDatasourceProperties = persistenceProperties.get(KEY_DATASOURCE);
if (ObjectUtils.isNotEmpty(persistenceDatasourceProperties) || !persistenceDatasourceProperties.isEmpty()) {
Binder binder = new Binder(new MapConfigurationPropertySource(persistenceDatasourceProperties));
dataSourceProperties = binder.bind(StringUtils.EMPTY, Bindable.of(DataSourceProperties.class)).get();
}
return dataSourceProperties;
}
// 獲取某個數(shù)據(jù)源對應(yīng)的MyBatis的配置類MybatisExtendProperties
public MybatisExtendProperties getPersistenceMybatisProperties(String persistenceName) {
MybatisExtendProperties mybatisProperties = new MybatisExtendProperties();
Map<String, Map<String, String>> persistenceProperties = multiPersistenceProperties.get(persistenceName);
Map<String, String> persistenceMybatisProperties = persistenceProperties.get(KEY_MYBATIS);
if (ObjectUtils.isNotEmpty(persistenceMybatisProperties) && !persistenceMybatisProperties.isEmpty()) {
Binder binder = new Binder(new MapConfigurationPropertySource(persistenceMybatisProperties));
mybatisProperties = binder.bind(StringUtils.EMPTY, Bindable.of(MybatisExtendProperties.class)).get();
}
return mybatisProperties;
}
}MultiPersistencePropertiesWrapper中僅提供了對數(shù)據(jù)源配置Map的set方法,然后提供了多個有具體含義的get方法,例如拿到數(shù)據(jù)源的個數(shù),名字集合以及某個數(shù)據(jù)源的DataSourceProperties或者MybatisExtendProperties,當(dāng)然,如何將某個數(shù)據(jù)源對應(yīng)的配置的Map解析為DataSourceProperties和MybatisExtendProperties,還是依靠的Binder,這里就不再贅述,可以自己看一下上述代碼的具體實現(xiàn)。
這里再多提一下,DataSourceProperties這個是Springboot提供的數(shù)據(jù)源的配置類,而MybatisExtendProperties是我們自定義的一個繼承于MybatisProperties(MyBatis官方啟動包提供)的配置類,主要是擴(kuò)展了一個叫做basePackage的屬性,用于配置映射接口路徑,如下所示。
public class MybatisExtendProperties extends MybatisProperties {
private String basePackage;
public String getBasePackage() {
return basePackage;
}
public void setBasePackage(String basePackage) {
this.basePackage = basePackage;
}
}現(xiàn)在回到LeeMultiPersistenceConfiguration的parseMultiPersistenceProperties() 方法,再貼出其實現(xiàn)如下。
private MultiPersistenceProperties parseMultiPersistenceProperties() {
MultiPersistenceProperties multiPersistenceProperties = new MultiPersistenceProperties();
// 將數(shù)據(jù)源相關(guān)的配置加載為MultiPersistencePropertiesWrapper
MultiPersistencePropertiesWrapper multiPersistencePropertiesWrapper = parseMultiPersistencePropertiesWrapper();
List<String> persistenceNames = multiPersistencePropertiesWrapper.getPersistenceNames();
// 遍歷每一個數(shù)據(jù)源并拿到這個數(shù)據(jù)源下數(shù)據(jù)源相關(guān)配置和MyBatis相關(guān)配置
for (String persistenceName : persistenceNames) {
DataSourceProperties dataSourceProperties = multiPersistencePropertiesWrapper
.getPersistenceDataSourceProperties(persistenceName);
MybatisExtendProperties mybatisProperties = multiPersistencePropertiesWrapper
.getPersistenceMybatisProperties(persistenceName);
// 添加當(dāng)前數(shù)據(jù)源的配置信息到MultiPersistenceProperties中
multiPersistenceProperties.addPersistenceProperties(
persistenceName, dataSourceProperties, mybatisProperties);
}
return multiPersistenceProperties;
}在完成所有數(shù)據(jù)源配置加載并且生成包裝類后,我們做的事情就是遍歷每一個數(shù)據(jù)源的名字,然后通過數(shù)據(jù)源名字從包裝類中拿到對應(yīng)的DataSourceProperties和MybatisExtendProperties,最后添加到MultiPersistenceProperties中,而MultiPersistenceProperties就是我們最終希望得到的多數(shù)據(jù)源的配置類,如下所示。
public class MultiPersistenceProperties {
private final Map<String, PersistenceProperties> persistencePropertiesMap = new HashMap<>(HASH_MAP_INITIAL_SIZE);
// 將DataSourceProperties和MybatisExtendProperties封裝為PersistenceProperties
public void addPersistenceProperties(String persistenceName,
DataSourceProperties dataSourceProperties,
MybatisExtendProperties mybatisProperties) {
PersistenceProperties persistenceProperties = new PersistenceProperties(dataSourceProperties, mybatisProperties);
persistencePropertiesMap.put(persistenceName, persistenceProperties);
}
public List<String> getPersistenceNames() {
return new ArrayList<>(persistencePropertiesMap.keySet());
}
public PersistenceProperties getPersistenceProperties(String persistenceName) {
return persistencePropertiesMap.get(persistenceName);
}
public DataSourceProperties getDataSourceProperties(String persistenceName) {
PersistenceProperties persistenceProperties = persistencePropertiesMap.get(persistenceName);
if (ObjectUtils.isNotEmpty(persistenceProperties)) {
return persistenceProperties.getDataSourceProperties();
}
throw new RuntimeException();
}
public MybatisExtendProperties getMybatisProperties(String persistenceName) {
PersistenceProperties persistenceProperties = persistencePropertiesMap.get(persistenceName);
if (ObjectUtils.isNotEmpty(persistenceProperties)) {
return persistenceProperties.getMybatisProperties();
}
throw new RuntimeException();
}
public static class PersistenceProperties {
private DataSourceProperties dataSourceProperties;
private MybatisExtendProperties mybatisProperties;
public PersistenceProperties(DataSourceProperties dataSourceProperties,
MybatisExtendProperties mybatisProperties) {
this.dataSourceProperties = dataSourceProperties;
this.mybatisProperties = mybatisProperties;
}
public DataSourceProperties getDataSourceProperties() {
return dataSourceProperties;
}
public void setDataSourceProperties(DataSourceProperties dataSourceProperties) {
this.dataSourceProperties = dataSourceProperties;
}
public MybatisExtendProperties getMybatisProperties() {
return mybatisProperties;
}
public void setMybatisProperties(MybatisExtendProperties mybatisProperties) {
this.mybatisProperties = mybatisProperties;
}
}
}我們在MultiPersistenceProperties中也進(jìn)行了適當(dāng)充血,首先將DataSourceProperties和MybatisExtendProperties封裝為了PersistenceProperties,然后將數(shù)據(jù)源名字作為key,數(shù)據(jù)源對應(yīng)的PersistenceProperties作為value,存儲到persistencePropertiesMap這個Map中,最后提供了若干get方法來實現(xiàn)對數(shù)據(jù)源對應(yīng)的PersistenceProperties的訪問。
那么至此,我們就完成了本節(jié)一開始定義的多數(shù)據(jù)源配置的加載,最終加載完畢后得到的多數(shù)據(jù)源的配置類就是MultiPersistenceProperties,并且數(shù)據(jù)源個數(shù),數(shù)據(jù)源名字和數(shù)據(jù)源類型完全可以自定義。
四. 數(shù)據(jù)源初始化
在第三節(jié)中已經(jīng)拿到了多數(shù)據(jù)源的配置信息,并且被我們解析為了一個易用性很強(qiáng)的配置類MultiPersistenceProperties,那么本節(jié)將介紹如何完成數(shù)據(jù)源的初始化,也就是如何創(chuàng)建數(shù)據(jù)源的bean并注冊到Spring容器中。
首先回到LeeMultiPersistenceConfiguration實現(xiàn)的registerBeanDefinitions() 方法,如下所示。
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
// 拿到多數(shù)據(jù)源配置類MultiPersistenceProperties
MultiPersistenceProperties multiPersistenceProperties = parseMultiPersistenceProperties();
List<String> persistenceNames = multiPersistenceProperties.getPersistenceNames();
for (String persistenceName : persistenceNames) {
// 注冊每個數(shù)據(jù)源對應(yīng)的數(shù)據(jù)源bean到Spring容器中
registerDatasource(registry, persistenceName, multiPersistenceProperties.getDataSourceProperties(persistenceName));
registerSqlSessionFactory(registry, persistenceName, multiPersistenceProperties.getMybatisProperties(persistenceName));
registerMapperScannerConfigurer(registry, persistenceName, multiPersistenceProperties.getMybatisProperties(persistenceName));
}
}跟進(jìn)registerDatasource() 方法,如下所示。
private void registerDatasource(BeanDefinitionRegistry registry,
String persistenceName,
DataSourceProperties dataSourceProperties) {
// 拿到具體數(shù)據(jù)源對應(yīng)的BeanDefinitionBuilder
// 如果沒有配置數(shù)據(jù)源類型則默認(rèn)是HikariCP
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(
ObjectUtils.isNotEmpty(dataSourceProperties.getType()) ? dataSourceProperties.getType() : DEFAULT_DATASOURCE_CLASS);
// 創(chuàng)建數(shù)據(jù)源的BeanDefinition并完成注冊
registry.registerBeanDefinition(persistenceName, beanDefinitionBuilder.getBeanDefinition());
}這里的注冊bean實際就是注冊BeanDefinition,依賴BeanDefinitionBuilder來創(chuàng)建對應(yīng)數(shù)據(jù)源的BeanDefinition,注意到這里好像僅僅只是將數(shù)據(jù)源的BeanDefinition創(chuàng)建出來然后就注冊到BeanDefinitionRegistry中了,并沒有進(jìn)行一些數(shù)據(jù)源的屬性相關(guān)的設(shè)置,那么數(shù)據(jù)源的屬性是怎么被設(shè)置的呢,還記得在第二節(jié)中我們通過LeeMultiPersistenceAutoConfiguration導(dǎo)入了一個叫做DataSourceBeanPostProcessor的bean后置處理器嗎,數(shù)據(jù)源的屬性的設(shè)置就是在這個后置處理器中完成的,下面一起看一下。
public class DataSourceBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 不同的數(shù)據(jù)源類型走不同的邏輯
if (bean instanceof HikariDataSource) {
assembleHikariDataSource((HikariDataSource) bean, beanName);
} else if (bean instanceof DruidDataSource) {
assembleDruidDatasource((DruidDataSource) bean, beanName);
} else if (bean instanceof DataSource) {
assembleTomcatJdbcDatasource((DataSource) bean, beanName);
}
return bean;
}
private void assembleHikariDataSource(HikariDataSource dataSource, String persistenceName) {
Map<String, String> persistenceDatasourceProperties = LeeMultiPersistenceConfiguration
.getPersistenceDatasourceProperties(persistenceName);
dataSource.setJdbcUrl(persistenceDatasourceProperties.get(KEY_URL));
Binder binder = new Binder(new MapConfigurationPropertySource(persistenceDatasourceProperties));
binder.bind(StringUtils.EMPTY, Bindable.ofInstance(dataSource));
}
private void assembleDruidDatasource(DruidDataSource dataSource, String persistenceName) {
Map<String, String> persistenceDatasourceProperties = LeeMultiPersistenceConfiguration
.getPersistenceDatasourceProperties(persistenceName);
Binder binder = new Binder(new MapConfigurationPropertySource(persistenceDatasourceProperties));
binder.bind(StringUtils.EMPTY, Bindable.ofInstance(dataSource));
}
private void assembleTomcatJdbcDatasource(DataSource dataSource, String persistenceName) {
Map<String, String> persistenceDatasourceProperties = LeeMultiPersistenceConfiguration
.getPersistenceDatasourceProperties(persistenceName);
Binder binder = new Binder(new MapConfigurationPropertySource(persistenceDatasourceProperties));
binder.bind(StringUtils.EMPTY, Bindable.ofInstance(dataSource));
}
}
// 在加載并解析數(shù)據(jù)源配置的時候?qū)ε渲眯畔⒆隽司彺?
public class LeeMultiPersistenceConfiguration implements ImportBeanDefinitionRegistrar, EnvironmentAware {
......
private static Map<String, Map<String, Map<String, String>>> persistencePropertiesCache;
public static Map<String, String> getPersistenceDatasourceProperties(String persistenceName) {
Map<String, Map<String, String>> persistenceProperties = persistencePropertiesCache.get(persistenceName);
return persistenceProperties.get(KEY_DATASOURCE);
}
}在DataSourceBeanPostProcessor中,僅針對類型為HikariDataSource,DruidDataSource或DataSource的bean生效,然后針對這些bean基于Binder完成屬性設(shè)置。因為在LeeMultiPersistenceConfiguration中加載數(shù)據(jù)源的配置時已經(jīng)對數(shù)據(jù)源配置信息做了緩存,所以現(xiàn)在可以直接通過LeeMultiPersistenceConfiguration拿到某個數(shù)據(jù)源對應(yīng)的配置信息。
那么至此,完整的數(shù)據(jù)源bean就注冊好了。
五. MyBatis初始化
同樣先回到LeeMultiPersistenceConfiguration實現(xiàn)的registerBeanDefinitions() 方法,如下所示。
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
// 拿到多數(shù)據(jù)源配置類MultiPersistenceProperties
MultiPersistenceProperties multiPersistenceProperties = parseMultiPersistenceProperties();
List<String> persistenceNames = multiPersistenceProperties.getPersistenceNames();
for (String persistenceName : persistenceNames) {
registerDatasource(registry, persistenceName, multiPersistenceProperties.getDataSourceProperties(persistenceName));
// 注冊每個數(shù)據(jù)源對應(yīng)的SqlSessionFactory到Spring容器中
registerSqlSessionFactory(registry, persistenceName, multiPersistenceProperties.getMybatisProperties(persistenceName));
// 注冊每個數(shù)據(jù)源對應(yīng)的MapperScannerConfigurer到Spring容器中
registerMapperScannerConfigurer(registry, persistenceName, multiPersistenceProperties.getMybatisProperties(persistenceName));
}
}現(xiàn)在先看一下registerSqlSessionFactory() 方法,如下所示。
private void registerSqlSessionFactory(BeanDefinitionRegistry registry,
String persistenceName,
MybatisExtendProperties mybatisProperties) {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
.genericBeanDefinition(SqlSessionFactoryBean.class);
// 為SqlSessionFactory添加數(shù)據(jù)源
// 主要就是指定數(shù)據(jù)源的名字
beanDefinitionBuilder.addPropertyReference(DATA_SOURCE, persistenceName);
// 設(shè)置SqlSessionFactory的配置文件路徑
beanDefinitionBuilder.addPropertyValue(CONFIG_LOCATION, mybatisProperties.getConfigLocation());
registry.registerBeanDefinition(BeanNameUtil.getSqlSessionFactoryName(persistenceName),
beanDefinitionBuilder.getBeanDefinition());
}上述方法也是通過BeanDefinitionBuilder來完成SqlSessionFactory對應(yīng)的BeanDefinition的創(chuàng)建,屬性設(shè)置和注冊。有兩點需要注意。
- 實際注冊的是SqlSessionFactoryBean的BeanDefinition。SqlSessionFactoryBean提供了更多豐富的配置來完成SqlSessionFactory的創(chuàng)建,例如可以設(shè)置引用的數(shù)據(jù)源名稱以及MyBatis的配置文件路徑等;
- 注冊的SqlSessionFactory的名字格式是固定的且為dataSourceName + SqlSessionFactory。這樣是為了方便MapperScannerConfigurer引用。
現(xiàn)在繼續(xù)看registerMapperScannerConfigurer() 方法,如下所示。
private void registerMapperScannerConfigurer(BeanDefinitionRegistry registry,
String persistenceName,
MybatisExtendProperties mybatisProperties) {
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
.genericBeanDefinition(MapperScannerConfigurer.class);
// 設(shè)置SqlSessionFactory
beanDefinitionBuilder.addPropertyValue(SQL_SESSION_FACTORY_BEANNAME, BeanNameUtil.getSqlSessionFactoryName(persistenceName));
// 設(shè)置映射接口的包路徑
beanDefinitionBuilder.addPropertyValue(BASE_PACKAGE, mybatisProperties.getBasePackage());
registry.registerBeanDefinition(BeanNameUtil.getMapperScannerConfigurerName(persistenceName),
beanDefinitionBuilder.getBeanDefinition());
}其實和注冊SqlSessionFactory是一樣的方式,唯一需要注意的就是在上述方法中為數(shù)據(jù)源對應(yīng)的MapperScannerConfigurer設(shè)置了SqlSessionFactory以及映射接口的路徑。
至此,MyBatis的初始化就做完了,其實就是向Spring容器注冊每個數(shù)據(jù)源對應(yīng)的SqlSessionFactory的bean以及MapperScannerConfigurer的bean。
六. Springboot數(shù)據(jù)源原生自動裝配抑制
由于我們自己定義了數(shù)據(jù)源的相關(guān)配置格式,那么相應(yīng)的用戶就不需要再去提供類似于spring.datasource這樣的配置,所以我們需要抑制Springboot的數(shù)據(jù)源的原生自動裝配的執(zhí)行,依賴的擴(kuò)展點是ApplicationContextInitializer。
如果熟悉Springboot的自動裝配,那么肯定對AutoConfigurationImportSelector不陌生,這個類的getAutoConfigurationEntry() 方法會拿到所有自動裝配的配置類的全限定名,如下所示。
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
// 獲取@EnableAutoConfiguration注解的元數(shù)據(jù)屬性
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 將需要自動裝配的組件的配置類的全限定名獲取出來
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 去除重復(fù)的組件
configurations = removeDuplicates(configurations);
// 去除被排除的組件
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
// 去除依賴項不滿足的組件
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
// 返回剩余的需要自動裝配的組件的配置類的全限定名
return new AutoConfigurationEntry(configurations, exclusions);
}其中getExclusions() 方法會拿到需要排除的自動裝配組件的全限定名,如下所示。
protected Set<String> getExclusions(AnnotationMetadata metadata, AnnotationAttributes attributes) {
Set<String> excluded = new LinkedHashSet<>();
excluded.addAll(asList(attributes, "exclude"));
excluded.addAll(asList(attributes, "excludeName"));
excluded.addAll(getExcludeAutoConfigurationsProperty());
return excluded;
}
protected List<String> getExcludeAutoConfigurationsProperty() {
Environment environment = getEnvironment();
if (environment == null) {
return Collections.emptyList();
}
if (environment instanceof ConfigurableEnvironment) {
Binder binder = Binder.get(environment);
// PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE為spring.autoconfigure.exclude
return binder.bind(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class).map(Arrays::asList)
.orElse(Collections.emptyList());
}
String[] excludes = environment.getProperty(PROPERTY_NAME_AUTOCONFIGURE_EXCLUDE, String[].class);
return (excludes != null) ? Arrays.asList(excludes) : Collections.emptyList();
}在獲取需要排除的自動裝配的組件的全限定名時,實際就是去Environment中通過spring.autoconfigure.exclude拿到需要排除的組件的全限定名,那么現(xiàn)在找到切入點了,只要在getExclusions() 方法執(zhí)行之前向Environment添加spring.autoconfigure.exclude的配置,那么就能夠排除指定自動裝配類的執(zhí)行,那么最合適的擴(kuò)展點其實就是ApplicationContextInitializer,理由如下。
- ApplicationContextInitializer的加載在初始化SpringApplication時就已經(jīng)完成;
- ApplicationContextInitializer的執(zhí)行是在prepareContext() 即準(zhǔn)備容器的時候,這個時候Environment已經(jīng)加載完畢,并且getExclusions() 方法也還沒執(zhí)行。
所以現(xiàn)在我們在spring.factories文件中加入如下內(nèi)容。
org.springframework.context.ApplicationContextInitializer=\ com.lee.multidatasource.initializer.ExcludeInitializer
然后ExcludeInitializer實現(xiàn)如下。
public class ExcludeInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final String EXCLUDE_PROPERTY_SOURCE_NAME = "EXCLUDE_PROPERTY_SOURCE_NAME";
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
ConfigurableEnvironment environment = applicationContext.getEnvironment();
Properties properties = new Properties();
properties.setProperty("spring.autoconfigure.exclude",
"org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration");
environment.getPropertySources().addLast(new PropertiesPropertySource(
EXCLUDE_PROPERTY_SOURCE_NAME, properties));
}
}至此,就完成了Springboot數(shù)據(jù)源原生自動裝配的抑制。
總結(jié)
本文對MyBatis多數(shù)據(jù)源Starter的實現(xiàn)進(jìn)行了說明,思維導(dǎo)圖如下所示。

以上就是一文搞懂MyBatis多數(shù)據(jù)源Starter實現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于MyBatis多數(shù)據(jù)源Starte的資料請關(guān)注腳本之家其它相關(guān)文章!
- MyBatisPuls多數(shù)據(jù)源操作數(shù)據(jù)源偶爾報錯問題
- Mybatis-plus配置多數(shù)據(jù)源,連接多數(shù)據(jù)庫方式
- MyBatis-Plus多數(shù)據(jù)源的示例代碼
- SpringBoot集成Mybatis實現(xiàn)對多數(shù)據(jù)源訪問原理
- Seata集成Mybatis-Plus解決多數(shù)據(jù)源事務(wù)問題
- 詳解SpringBoot Mybatis如何對接多數(shù)據(jù)源
- Mybatis操作多數(shù)據(jù)源的實現(xiàn)
- Mybatis-plus多數(shù)據(jù)源配置的兩種方式總結(jié)
- MyBatis-Plus 集成動態(tài)多數(shù)據(jù)源的實現(xiàn)示例
- Mybatis-Plus的多數(shù)據(jù)源你了解嗎
- mybatis-flex實現(xiàn)多數(shù)據(jù)源操作
相關(guān)文章
Mybatis中<if>和<choose>的區(qū)別及“=”判斷方式
這篇文章主要介紹了Mybatis中<if>和<choose>的區(qū)別及“=”判斷方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06
IDEA:Error running,Command line is too&n
這篇文章主要介紹了IDEA:Error running,Command line is too long.解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07
springboot swagger 接口文檔分組展示功能實現(xiàn)
這篇文章主要介紹了springboot swagger 接口文檔分組展示功能實現(xiàn),本文通過實例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-03-03
public?static?void?main(String[]?args)使用解讀
這篇文章主要介紹了public?static?void?main(String[]?args)的使用,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
springboot使用DynamicDataSource動態(tài)切換數(shù)據(jù)源的實現(xiàn)過程
這篇文章主要給大家介紹了關(guān)于springboot使用DynamicDataSource動態(tài)切換數(shù)據(jù)源的實現(xiàn)過程,Spring Boot應(yīng)用中可以配置多個數(shù)據(jù)源,并根據(jù)注解靈活指定當(dāng)前使用的數(shù)據(jù)源,需要的朋友可以參考下2023-08-08
手?jǐn)]一個Spring?Boot?Starter并上傳到Maven中央倉庫
本文主要介紹了手?jǐn)]一個Spring?Boot?Starter并上傳到Maven中央倉庫,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05

