您好,登錄后才能下訂單哦!
這篇文章主要介紹“MyBatis多數(shù)據(jù)源Starter怎么實(shí)現(xiàn)”,在日常操作中,相信很多人在MyBatis多數(shù)據(jù)源Starter怎么實(shí)現(xiàn)問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”MyBatis多數(shù)據(jù)源Starter怎么實(shí)現(xiàn)”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!
要實(shí)現(xiàn)Starter包,肯定需要借助Springboot的自動(dòng)裝配機(jī)制,所以我們首先需要提供自動(dòng)裝配的配置類。
然后我們需要加載多個(gè)數(shù)據(jù)源的配置并且生成對(duì)應(yīng)的數(shù)據(jù)源,同時(shí)還需要可以根據(jù)用戶配置的type創(chuàng)建不同的數(shù)據(jù)源,例如可以支持創(chuàng)建HikariCP,Druid和TomcatJdbc的數(shù)據(jù)源。
創(chuàng)建出來的數(shù)據(jù)源需要根據(jù)用戶的配置,設(shè)置給不同的SqlSessionFactory,然后不同的SqlSessionFactory設(shè)置給不同的MapperScannerConfigurer,最終實(shí)現(xiàn)的效果就是一部分映射接口使用一個(gè)數(shù)據(jù)源,另一部分映射接口使用另一個(gè)數(shù)據(jù)源。
最后,還需要提供一種手段,抑制Springboot原生的數(shù)據(jù)源加載,這個(gè)功能我們可以通過ApplicationContextInitializer這個(gè)擴(kuò)展點(diǎn)來完成。
整體的一個(gè)思維導(dǎo)圖如下所示。
由于適配的是Springboot的2.7.x版本,所以需要在resources\META-INF\spring目錄下創(chuàng)建org.springframework.boot.autoconfigure.AutoConfiguration.imports文件,且內(nèi)容如下所示。
com.lee.multidatasource.autoconfig.LeeMultiPersistenceAutoConfiguration
上述的LeeMultiPersistenceAutoConfiguration就是完成自動(dòng)裝配的配置類,實(shí)現(xiàn)如下。
@AutoConfiguration @Import({LeeMultiPersistenceConfiguration.class, DataSourceBeanPostProcessor.class,}) public class LeeMultiPersistenceAutoConfiguration {}
通過LeeMultiPersistenceAutoConfiguration導(dǎo)入了兩個(gè)bean,一個(gè)是LeeMultiPersistenceConfiguration,用于加載配置以及創(chuàng)建數(shù)據(jù)源和MyBatis的bean,另一個(gè)是DataSourceBeanPostProcessor,用于對(duì)LeeMultiPersistenceConfiguration創(chuàng)建的bean做一些后置處理。
上述就是自動(dòng)裝配的實(shí)現(xiàn),主要就是將LeeMultiPersistenceAutoConfiguration注冊(cè)到容器中,而LeeMultiPersistenceAutoConfiguration也是整個(gè)Starter包實(shí)現(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的下一級(jí)的配置,是數(shù)據(jù)源的名字,可以由用戶自定義,這個(gè)名字最終會(huì)作為數(shù)據(jù)源的bean在IOC容器中的名字。
在lee.persistence.dataSourceName的下一級(jí)的配置,固定是兩個(gè)配置項(xiàng),其一是lee.persistence.dataSourceName.datasource,用于設(shè)置數(shù)據(jù)源相關(guān)的配置,其二是lee.persistence.dataSourceName.mybatis,用于設(shè)置MyBatis相關(guān)的配置。
由于數(shù)據(jù)源的名字和個(gè)數(shù)都可以由用戶自定義,那么很難基于@ConfigurationProperties注解來一步到位的完成上述數(shù)據(jù)源配置的加載,我們需要基于Environment來自行處理。
下面來看一下用于處理數(shù)據(jù)源配置的LeeMultiPersistenceConfiguration的類圖,如下所示。
LeeMultiPersistenceConfiguration首先實(shí)現(xiàn)了EnvironmentAware接口,從而可以拿到Environment對(duì)象,其次實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口,從而可以向Spring注冊(cè)BeanDefinition,那么實(shí)際上LeeMultiPersistenceConfiguration做的事情就是加載配置以及向Spring注冊(cè)數(shù)據(jù)源和MyBatis相關(guān)組件的BeanDefinition。下面看一下LeeMultiPersistenceConfiguration實(shí)現(xiàn)的registerBeanDefinitions() 方法。
@Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 加載并解析數(shù)據(jù)源配置 MultiPersistenceProperties multiPersistenceProperties = parseMultiPersistenceProperties(); List<String> persistenceNames = multiPersistenceProperties.getPersistenceNames(); // 為每個(gè)數(shù)據(jù)源注冊(cè)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(); // 遍歷每一個(gè)數(shù)據(jù)源并拿到這個(gè)數(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的形式,因?yàn)?strong>lee.persistence這個(gè)配置前綴已經(jīng)是確定的,所以在Binder的bind() 方法中傳入的配置名就是lee.persistence,那么最終得到的配置的Map實(shí)際內(nèi)容如下所示。
// 數(shù)據(jù)源名就是上面示例的dataSourceName1 // 配置類型就是datasource或mybatis Map[數(shù)據(jù)源名, Map[配置類型, Map[配置key, 配置value]]]
通常上述這種層級(jí)很多的Map,無論是可讀性還是易用性都很差,所以我們可以使用一個(gè)包裝類來包裝一下這種Map,并進(jìn)行適當(dāng)充血,來方便對(duì)這種多層級(jí)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; } // 只對(duì)multiPersistenceProperties提供set方法 public void setMultiPersistenceProperties(Map<String, Map<String, Map<String, String>>> multiPersistenceProperties) { this.multiPersistenceProperties = multiPersistenceProperties; } // 獲取數(shù)據(jù)源的個(gè)數(shù) public int getPersistenceSize() { return multiPersistenceProperties.size(); } // 獲取所有數(shù)據(jù)源的名字 public List<String> getPersistenceNames() { return new ArrayList<>(multiPersistenceProperties.keySet()); } // 獲取某個(gè)數(shù)據(jù)源對(duì)應(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; } // 獲取某個(gè)數(shù)據(jù)源對(duì)應(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中僅提供了對(duì)數(shù)據(jù)源配置Map的set方法,然后提供了多個(gè)有具體含義的get方法,例如拿到數(shù)據(jù)源的個(gè)數(shù),名字集合以及某個(gè)數(shù)據(jù)源的DataSourceProperties或者MybatisExtendProperties,當(dāng)然,如何將某個(gè)數(shù)據(jù)源對(duì)應(yīng)的配置的Map解析為DataSourceProperties和MybatisExtendProperties,還是依靠的Binder,這里就不再贅述,可以自己看一下上述代碼的具體實(shí)現(xiàn)。
這里再多提一下,DataSourceProperties這個(gè)是Springboot提供的數(shù)據(jù)源的配置類,而MybatisExtendProperties是我們自定義的一個(gè)繼承于MybatisProperties(MyBatis官方啟動(dòng)包提供)的配置類,主要是擴(kuò)展了一個(gè)叫做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() 方法,再貼出其實(shí)現(xiàn)如下。
private MultiPersistenceProperties parseMultiPersistenceProperties() { MultiPersistenceProperties multiPersistenceProperties = new MultiPersistenceProperties(); // 將數(shù)據(jù)源相關(guān)的配置加載為MultiPersistencePropertiesWrapper MultiPersistencePropertiesWrapper multiPersistencePropertiesWrapper = parseMultiPersistencePropertiesWrapper(); List<String> persistenceNames = multiPersistencePropertiesWrapper.getPersistenceNames(); // 遍歷每一個(gè)數(shù)據(jù)源并拿到這個(gè)數(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ù)源配置加載并且生成包裝類后,我們做的事情就是遍歷每一個(gè)數(shù)據(jù)源的名字,然后通過數(shù)據(jù)源名字從包裝類中拿到對(duì)應(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; } } }
我們?cè)?strong>MultiPersistenceProperties中也進(jìn)行了適當(dāng)充血,首先將DataSourceProperties和MybatisExtendProperties封裝為了PersistenceProperties,然后將數(shù)據(jù)源名字作為key,數(shù)據(jù)源對(duì)應(yīng)的PersistenceProperties作為value,存儲(chǔ)到persistencePropertiesMap這個(gè)Map中,最后提供了若干get方法來實(shí)現(xiàn)對(duì)數(shù)據(jù)源對(duì)應(yīng)的PersistenceProperties的訪問。
那么至此,我們就完成了本節(jié)一開始定義的多數(shù)據(jù)源配置的加載,最終加載完畢后得到的多數(shù)據(jù)源的配置類就是MultiPersistenceProperties,并且數(shù)據(jù)源個(gè)數(shù),數(shù)據(jù)源名字和數(shù)據(jù)源類型完全可以自定義。
在第三節(jié)中已經(jīng)拿到了多數(shù)據(jù)源的配置信息,并且被我們解析為了一個(gè)易用性很強(qiáng)的配置類MultiPersistenceProperties,那么本節(jié)將介紹如何完成數(shù)據(jù)源的初始化,也就是如何創(chuàng)建數(shù)據(jù)源的bean并注冊(cè)到Spring容器中。
首先回到LeeMultiPersistenceConfiguration實(shí)現(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) { // 注冊(cè)每個(gè)數(shù)據(jù)源對(duì)應(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ù)源對(duì)應(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并完成注冊(cè) registry.registerBeanDefinition(persistenceName, beanDefinitionBuilder.getBeanDefinition()); }
這里的注冊(cè)bean實(shí)際就是注冊(cè)BeanDefinition,依賴BeanDefinitionBuilder來創(chuàng)建對(duì)應(yīng)數(shù)據(jù)源的BeanDefinition,注意到這里好像僅僅只是將數(shù)據(jù)源的BeanDefinition創(chuàng)建出來然后就注冊(cè)到BeanDefinitionRegistry中了,并沒有進(jìn)行一些數(shù)據(jù)源的屬性相關(guān)的設(shè)置,那么數(shù)據(jù)源的屬性是怎么被設(shè)置的呢,還記得在第二節(jié)中我們通過LeeMultiPersistenceAutoConfiguration導(dǎo)入了一個(gè)叫做DataSourceBeanPostProcessor的bean后置處理器嗎,數(shù)據(jù)源的屬性的設(shè)置就是在這個(gè)后置處理器中完成的,下面一起看一下。
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ù)源配置的時(shí)候?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中,僅針對(duì)類型為HikariDataSource,DruidDataSource或DataSource的bean生效,然后針對(duì)這些bean基于Binder完成屬性設(shè)置。因?yàn)樵?strong>LeeMultiPersistenceConfiguration中加載數(shù)據(jù)源的配置時(shí)已經(jīng)對(duì)數(shù)據(jù)源配置信息做了緩存,所以現(xiàn)在可以直接通過LeeMultiPersistenceConfiguration拿到某個(gè)數(shù)據(jù)源對(duì)應(yīng)的配置信息。
那么至此,完整的數(shù)據(jù)源bean就注冊(cè)好了。
同樣先回到LeeMultiPersistenceConfiguration實(shí)現(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)); // 注冊(cè)每個(gè)數(shù)據(jù)源對(duì)應(yīng)的SqlSessionFactory到Spring容器中 registerSqlSessionFactory(registry, persistenceName, multiPersistenceProperties.getMybatisProperties(persistenceName)); // 注冊(cè)每個(gè)數(shù)據(jù)源對(duì)應(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對(duì)應(yīng)的BeanDefinition的創(chuàng)建,屬性設(shè)置和注冊(cè)。有兩點(diǎn)需要注意。
實(shí)際注冊(cè)的是SqlSessionFactoryBean的BeanDefinition。SqlSessionFactoryBean提供了更多豐富的配置來完成SqlSessionFactory的創(chuàng)建,例如可以設(shè)置引用的數(shù)據(jù)源名稱以及MyBatis的配置文件路徑等;
注冊(cè)的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()); }
其實(shí)和注冊(cè)SqlSessionFactory是一樣的方式,唯一需要注意的就是在上述方法中為數(shù)據(jù)源對(duì)應(yīng)的MapperScannerConfigurer設(shè)置了SqlSessionFactory以及映射接口的路徑。
至此,MyBatis的初始化就做完了,其實(shí)就是向Spring容器注冊(cè)每個(gè)數(shù)據(jù)源對(duì)應(yīng)的SqlSessionFactory的bean以及MapperScannerConfigurer的bean。
由于我們自己定義了數(shù)據(jù)源的相關(guān)配置格式,那么相應(yīng)的用戶就不需要再去提供類似于spring.datasource這樣的配置,所以我們需要抑制Springboot的數(shù)據(jù)源的原生自動(dòng)裝配的執(zhí)行,依賴的擴(kuò)展點(diǎn)是ApplicationContextInitializer。
如果熟悉Springboot的自動(dòng)裝配,那么肯定對(duì)AutoConfigurationImportSelector不陌生,這個(gè)類的getAutoConfigurationEntry() 方法會(huì)拿到所有自動(dòng)裝配的配置類的全限定名,如下所示。
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } // 獲取@EnableAutoConfiguration注解的元數(shù)據(jù)屬性 AnnotationAttributes attributes = getAttributes(annotationMetadata); // 將需要自動(dòng)裝配的組件的配置類的全限定名獲取出來 List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); // 去除重復(fù)的組件 configurations = removeDuplicates(configurations); // 去除被排除的組件 Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); // 去除依賴項(xiàng)不滿足的組件 configurations = getConfigurationClassFilter().filter(configurations); fireAutoConfigurationImportEvents(configurations, exclusions); // 返回剩余的需要自動(dòng)裝配的組件的配置類的全限定名 return new AutoConfigurationEntry(configurations, exclusions); }
其中getExclusions() 方法會(huì)拿到需要排除的自動(dòng)裝配組件的全限定名,如下所示。
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(); }
在獲取需要排除的自動(dòng)裝配的組件的全限定名時(shí),實(shí)際就是去Environment中通過spring.autoconfigure.exclude拿到需要排除的組件的全限定名,那么現(xiàn)在找到切入點(diǎn)了,只要在getExclusions() 方法執(zhí)行之前向Environment添加spring.autoconfigure.exclude的配置,那么就能夠排除指定自動(dòng)裝配類的執(zhí)行,那么最合適的擴(kuò)展點(diǎn)其實(shí)就是ApplicationContextInitializer,理由如下。
ApplicationContextInitializer的加載在初始化SpringApplication時(shí)就已經(jīng)完成;
ApplicationContextInitializer的執(zhí)行是在prepareContext() 即準(zhǔn)備容器的時(shí)候,這個(gè)時(shí)候Environment已經(jīng)加載完畢,并且getExclusions() 方法也還沒執(zhí)行。
所以現(xiàn)在我們?cè)?strong>spring.factories文件中加入如下內(nèi)容。
org.springframework.context.ApplicationContextInitializer=\ com.lee.multidatasource.initializer.ExcludeInitializer
然后ExcludeInitializer實(shí)現(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)); } }
到此,關(guān)于“MyBatis多數(shù)據(jù)源Starter怎么實(shí)現(xiàn)”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。