91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

MyBatis多數據源Starter怎么實現

發布時間:2023-04-19 16:18:51 來源:億速云 閱讀:130 作者:iii 欄目:開發技術

這篇文章主要介紹“MyBatis多數據源Starter怎么實現”,在日常操作中,相信很多人在MyBatis多數據源Starter怎么實現問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”MyBatis多數據源Starter怎么實現”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

    一. 實現思路

    要實現Starter包,肯定需要借助Springboot的自動裝配機制,所以我們首先需要提供自動裝配的配置類。

    然后我們需要加載多個數據源的配置并且生成對應的數據源,同時還需要可以根據用戶配置的type創建不同的數據源,例如可以支持創建HikariCPDruidTomcatJdbc的數據源。

    創建出來的數據源需要根據用戶的配置,設置給不同的SqlSessionFactory,然后不同的SqlSessionFactory設置給不同的MapperScannerConfigurer,最終實現的效果就是一部分映射接口使用一個數據源,另一部分映射接口使用另一個數據源。

    最后,還需要提供一種手段,抑制Springboot原生的數據源加載,這個功能我們可以通過ApplicationContextInitializer這個擴展點來完成。

    整體的一個思維導圖如下所示。

    MyBatis多數據源Starter怎么實現

    二. 自動裝配實現

    由于適配的是Springboot2.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,用于加載配置以及創建數據源和MyBatisbean,另一個是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的下一級的配置,是數據源的名字,可以由用戶自定義,這個名字最終會作為數據源的beanIOC容器中的名字。

    lee.persistence.dataSourceName的下一級的配置,固定是兩個配置項,其一是lee.persistence.dataSourceName.datasource,用于設置數據源相關的配置,其二是lee.persistence.dataSourceName.mybatis,用于設置MyBatis相關的配置。

    由于數據源的名字和個數都可以由用戶自定義,那么很難基于@ConfigurationProperties注解來一步到位的完成上述數據源配置的加載,我們需要基于Environment來自行處理。

    下面來看一下用于處理數據源配置的LeeMultiPersistenceConfiguration的類圖,如下所示。

    MyBatis多數據源Starter怎么實現

    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));
        }
    }

    本節就先分析一下數據源配置的加載和解析,具體就是LeeMultiPersistenceConfigurationparseMultiPersistenceProperties() 方法,如下所示。

    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這個配置前綴已經是確定的,所以在Binderbind() 方法中傳入的配置名就是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中僅提供了對數據源配置Mapset方法,然后提供了多個有具體含義的get方法,例如拿到數據源的個數,名字集合以及某個數據源的DataSourceProperties或者MybatisExtendProperties,當然,如何將某個數據源對應的配置的Map解析為DataSourcePropertiesMybatisExtendProperties,還是依靠的Binder,這里就不再贅述,可以自己看一下上述代碼的具體實現。

    這里再多提一下,DataSourceProperties這個是Springboot提供的數據源的配置類,而MybatisExtendProperties是我們自定義的一個繼承于MybatisPropertiesMyBatis官方啟動包提供)的配置類,主要是擴展了一個叫做basePackage的屬性,用于配置映射接口路徑,如下所示。

    public class MybatisExtendProperties extends MybatisProperties {
    
        private String basePackage;
    
        public String getBasePackage() {
            return basePackage;
        }
    
        public void setBasePackage(String basePackage) {
            this.basePackage = basePackage;
        }
    
    }

    現在回到LeeMultiPersistenceConfigurationparseMultiPersistenceProperties() 方法,再貼出其實現如下。

    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;
    }

    在完成所有數據源配置加載并且生成包裝類后,我們做的事情就是遍歷每一個數據源的名字,然后通過數據源名字從包裝類中拿到對應的DataSourcePropertiesMybatisExtendProperties,最后添加到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中也進行了適當充血,首先將DataSourcePropertiesMybatisExtendProperties封裝為了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導入了一個叫做DataSourceBeanPostProcessorbean后置處理器嗎,數據源的屬性的設置就是在這個后置處理器中完成的,下面一起看一下。

    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中,僅針對類型為HikariDataSourceDruidDataSourceDataSourcebean生效,然后針對這些bean基于Binder完成屬性設置。因為在LeeMultiPersistenceConfiguration中加載數據源的配置時已經對數據源配置信息做了緩存,所以現在可以直接通過LeeMultiPersistenceConfiguration拿到某個數據源對應的配置信息。

    那么至此,完整的數據源bean就注冊好了。

    五. MyBatis初始化

    同樣先回到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的創建,屬性設置和注冊。有兩點需要注意。

    • 實際注冊的是SqlSessionFactoryBeanBeanDefinitionSqlSessionFactoryBean提供了更多豐富的配置來完成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容器注冊每個數據源對應的SqlSessionFactorybean以及MapperScannerConfigurerbean

    六. Springboot數據源原生自動裝配抑制

    由于我們自己定義了數據源的相關配置格式,那么相應的用戶就不需要再去提供類似于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怎么實現”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    海丰县| 龙州县| 宁南县| 富阳市| 河南省| 彰武县| 高安市| 松桃| 潞西市| 延川县| 长沙县| 高青县| 贵港市| 盱眙县| 崇仁县| 湛江市| 府谷县| 万州区| 新河县| 柳江县| 皋兰县| 甘肃省| 阳西县| 仲巴县| 天峻县| 霍邱县| 湘潭县| 泰州市| 宿州市| 东至县| 万年县| 日喀则市| 化德县| 上思县| 安远县| 宝应县| 黄大仙区| 怀安县| 成安县| 武胜县| 新野县|