欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文搞懂MyBatis多數(shù)據(jù)源Starter實(shí)現(xiàn)

 更新時(shí)間:2023年04月19日 09:46:59   作者:半夏之沫  
本文將實(shí)現(xiàn)一個(gè)MyBatis的Springboot的Starter包,引用這個(gè)Starter包后,僅需要提供少量配置信息,就能夠完成MyBatis多數(shù)據(jù)源的初始化和使用,需要的小伙伴可以參考一下

前言

本文將實(shí)現(xiàn)一個(gè)MyBatisSpringbootStarter包,引用這個(gè)Starter包后,僅需要提供少量配置信息,就能夠完成MyBatis多數(shù)據(jù)源的初始化和使用,相較于MyBatis官方的Starter包,擴(kuò)展了多數(shù)據(jù)源的使用場(chǎng)景。

本文的所有源碼可以從如下倉(cāng)庫(kù)下載。

multidatasource GitHub

Springboot版本:2.7.6

正文

一. 實(shí)現(xiàn)思路

要實(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,DruidTomcatJdbc的數(shù)據(jù)源。

創(chuàng)建出來(lái)的數(shù)據(jù)源需要根據(jù)用戶的配置,設(shè)置給不同的SqlSessionFactory,然后不同的SqlSessionFactory設(shè)置給不同的MapperScannerConfigurer,最終實(shí)現(xiàn)的效果就是一部分映射接口使用一個(gè)數(shù)據(jù)源,另一部分映射接口使用另一個(gè)數(shù)據(jù)源。

最后,還需要提供一種手段,抑制Springboot原生的數(shù)據(jù)源加載,這個(gè)功能我們可以通過(guò)ApplicationContextInitializer這個(gè)擴(kuò)展點(diǎn)來(lái)完成。

整體的一個(gè)思維導(dǎo)圖如下所示。

二. 自動(dòng)裝配實(shí)現(xiàn)

由于適配的是Springboot2.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 {}

通過(guò)LeeMultiPersistenceAutoConfiguration導(dǎo)入了兩個(gè)bean,一個(gè)是LeeMultiPersistenceConfiguration,用于加載配置以及創(chuàng)建數(shù)據(jù)源和MyBatisbean,另一個(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ù)源的beanIOC容器中的名字。

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注解來(lái)一步到位的完成上述數(shù)據(jù)源配置的加載,我們需要基于Environment來(lái)自行處理。

下面來(lái)看一下用于處理數(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ù)源配置的加載和解析,具體就是LeeMultiPersistenceConfigurationparseMultiPersistenceProperties() 方法,如下所示。

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)是確定的,所以在Binderbind() 方法中傳入的配置名就是lee.persistence,那么最終得到的配置的Map實(shí)際內(nèi)容如下所示。

// 數(shù)據(jù)源名就是上面示例的dataSourceName1
// 配置類型就是datasource或mybatis
Map[數(shù)據(jù)源名, Map[配置類型, Map[配置key, 配置value]]]

通常上述這種層級(jí)很多的Map,無(wú)論是可讀性還是易用性都很差,所以我們可以使用一個(gè)包裝類來(lái)包裝一下這種Map,并進(jìn)行適當(dāng)充血,來(lái)方便對(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ù)源配置Mapset方法,然后提供了多個(gè)有具體含義的get方法,例如拿到數(shù)據(jù)源的個(gè)數(shù),名字集合以及某個(gè)數(shù)據(jù)源的DataSourceProperties或者MybatisExtendProperties,當(dāng)然,如何將某個(gè)數(shù)據(jù)源對(duì)應(yīng)的配置的Map解析為DataSourcePropertiesMybatisExtendProperties,還是依靠的Binder,這里就不再贅述,可以自己看一下上述代碼的具體實(shí)現(xiàn)。

這里再多提一下,DataSourceProperties這個(gè)是Springboot提供的數(shù)據(jù)源的配置類,而MybatisExtendProperties是我們自定義的一個(gè)繼承于MybatisPropertiesMyBatis官方啟動(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)在回到LeeMultiPersistenceConfigurationparseMultiPersistenceProperties() 方法,再貼出其實(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ù)源的名字,然后通過(guò)數(shù)據(jù)源名字從包裝類中拿到對(duì)應(yīng)的DataSourcePropertiesMybatisExtendProperties,最后添加到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)充血,首先將DataSourcePropertiesMybatisExtendProperties封裝為了PersistenceProperties,然后將數(shù)據(jù)源名字作為key,數(shù)據(jù)源對(duì)應(yīng)的PersistenceProperties作為value,存儲(chǔ)到persistencePropertiesMap這個(gè)Map中,最后提供了若干get方法來(lái)實(shí)現(xiàn)對(duì)數(shù)據(jù)源對(duì)應(yīng)的PersistenceProperties的訪問(wèn)。

那么至此,我們就完成了本節(jié)一開始定義的多數(shù)據(jù)源配置的加載,最終加載完畢后得到的多數(shù)據(jù)源的配置類就是MultiPersistenceProperties,并且數(shù)據(jù)源個(gè)數(shù),數(shù)據(jù)源名字和數(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
    // 如果沒(méi)有配置數(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來(lái)創(chuàng)建對(duì)應(yīng)數(shù)據(jù)源的BeanDefinition,注意到這里好像僅僅只是將數(shù)據(jù)源的BeanDefinition創(chuàng)建出來(lái)然后就注冊(cè)到BeanDefinitionRegistry中了,并沒(méi)有進(jìn)行一些數(shù)據(jù)源的屬性相關(guān)的設(shè)置,那么數(shù)據(jù)源的屬性是怎么被設(shè)置的呢,還記得在第二節(jié)中我們通過(guò)LeeMultiPersistenceAutoConfiguration導(dǎo)入了一個(gè)叫做DataSourceBeanPostProcessorbean后置處理器嗎,數(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,DruidDataSourceDataSourcebean生效,然后針對(duì)這些bean基于Binder完成屬性設(shè)置。因?yàn)樵?strong>LeeMultiPersistenceConfiguration中加載數(shù)據(jù)源的配置時(shí)已經(jīng)對(duì)數(shù)據(jù)源配置信息做了緩存,所以現(xiàn)在可以直接通過(guò)LeeMultiPersistenceConfiguration拿到某個(gè)數(shù)據(jù)源對(duì)應(yīng)的配置信息。

那么至此,完整的數(shù)據(jù)源bean就注冊(cè)好了。

五. MyBatis初始化

同樣先回到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());
}

上述方法也是通過(guò)BeanDefinitionBuilder來(lái)完成SqlSessionFactory對(duì)應(yīng)的BeanDefinition的創(chuàng)建,屬性設(shè)置和注冊(cè)。有兩點(diǎn)需要注意。

  • 實(shí)際注冊(cè)的是SqlSessionFactoryBeanBeanDefinitionSqlSessionFactoryBean提供了更多豐富的配置來(lái)完成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)的SqlSessionFactorybean以及MapperScannerConfigurerbean

六. Springboot數(shù)據(jù)源原生自動(dòng)裝配抑制

由于我們自己定義了數(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)裝配的組件的配置類的全限定名獲取出來(lái)
    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中通過(guò)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() 方法也還沒(méi)執(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));
    }

}

至此,就完成了Springboot數(shù)據(jù)源原生自動(dòng)裝配的抑制。

總結(jié)

本文對(duì)MyBatis多數(shù)據(jù)源Starter的實(shí)現(xiàn)進(jìn)行了說(shuō)明,思維導(dǎo)圖如下所示。

以上就是一文搞懂MyBatis多數(shù)據(jù)源Starter實(shí)現(xiàn)的詳細(xì)內(nèi)容,更多關(guān)于MyBatis多數(shù)據(jù)源Starte的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Spring boot怎么整合Mybatis

    Spring boot怎么整合Mybatis

    spring boot的簡(jiǎn)配置方便的開發(fā),下面通過(guò)本文給大家分享Spring boot整合Mybatis的方法,需要的朋友參考下
    2017-07-07
  • Mybatis中<if>和<choose>的區(qū)別及“=”判斷方式

    Mybatis中<if>和<choose>的區(qū)別及“=”判斷方式

    這篇文章主要介紹了Mybatis中<if>和<choose>的區(qū)別及“=”判斷方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Spring的Ioc模擬實(shí)現(xiàn)詳細(xì)介紹

    Spring的Ioc模擬實(shí)現(xiàn)詳細(xì)介紹

    這篇文章主要介紹了Spring的Ioc模擬實(shí)現(xiàn)詳細(xì)介紹,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • IDEA:Error running,Command line is too long.解決方案

    IDEA:Error running,Command line is too&n

    這篇文章主要介紹了IDEA:Error running,Command line is too long.解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • springboot swagger 接口文檔分組展示功能實(shí)現(xiàn)

    springboot swagger 接口文檔分組展示功能實(shí)現(xiàn)

    這篇文章主要介紹了springboot swagger 接口文檔分組展示功能實(shí)現(xiàn),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • public?static?void?main(String[]?args)使用解讀

    public?static?void?main(String[]?args)使用解讀

    這篇文章主要介紹了public?static?void?main(String[]?args)的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • java.lang.Void類的解析與使用詳解

    java.lang.Void類的解析與使用詳解

    這篇文章主要介紹了java.lang.Void類的解析與使用詳解,文中涉及到了java.lang.integer類的源碼,分場(chǎng)景給大家介紹的非常詳細(xì),給大家補(bǔ)充介紹java.lang.Void 與 void的比較及使用,需要的朋友可以參考下
    2017-12-12
  • springboot使用DynamicDataSource動(dòng)態(tài)切換數(shù)據(jù)源的實(shí)現(xiàn)過(guò)程

    springboot使用DynamicDataSource動(dòng)態(tài)切換數(shù)據(jù)源的實(shí)現(xiàn)過(guò)程

    這篇文章主要給大家介紹了關(guān)于springboot使用DynamicDataSource動(dòng)態(tài)切換數(shù)據(jù)源的實(shí)現(xiàn)過(guò)程,Spring Boot應(yīng)用中可以配置多個(gè)數(shù)據(jù)源,并根據(jù)注解靈活指定當(dāng)前使用的數(shù)據(jù)源,需要的朋友可以參考下
    2023-08-08
  • 手?jǐn)]一個(gè)Spring?Boot?Starter并上傳到Maven中央倉(cāng)庫(kù)

    手?jǐn)]一個(gè)Spring?Boot?Starter并上傳到Maven中央倉(cāng)庫(kù)

    本文主要介紹了手?jǐn)]一個(gè)Spring?Boot?Starter并上傳到Maven中央倉(cāng)庫(kù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • Java引用類型interface的用法總結(jié)

    Java引用類型interface的用法總結(jié)

    這篇文章主要為大家詳細(xì)介紹了Java中引用類型interface的用法的相關(guān)資料,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Java有一定幫助,感興趣的可以了解一下
    2022-10-10

最新評(píng)論