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