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

SpringBoot配置動(dòng)態(tài)數(shù)據(jù)源的實(shí)戰(zhàn)詳解

 更新時(shí)間:2024年08月25日 14:25:41   作者:凡人編程傳  
Spring對(duì)數(shù)據(jù)源的管理類似于策略模式,不懂策略模式也沒關(guān)系,其實(shí)就是有一個(gè)全局的鍵值對(duì),類型是Map<String, DataSource>,當(dāng)JDBC操作數(shù)據(jù)庫(kù)之時(shí),會(huì)根據(jù)不同的key值選擇不同的數(shù)據(jù)源,本文介紹了SpringBoot配置動(dòng)態(tài)數(shù)據(jù)源的方法,需要的朋友可以參考下

數(shù)據(jù)源切換方法

Spring對(duì)數(shù)據(jù)源的管理類似于策略模式,不懂策略模式也沒關(guān)系,其實(shí)就是有一個(gè)全局的鍵值對(duì),類型是Map<String, DataSource>。當(dāng)JDBC操作數(shù)據(jù)庫(kù)之時(shí),會(huì)根據(jù)不同的key值選擇不同的數(shù)據(jù)源。而這個(gè)key值可以放到方法的注解里。

所以切換數(shù)據(jù)源的思路就是讓JDBC在獲取數(shù)據(jù)源時(shí)根據(jù)key獲取到要切換的數(shù)據(jù)源。

JDBC提供了AbstractRoutingDataSource抽象類,類名意思是數(shù)據(jù)源路由,該類提供了一個(gè)抽象方法determineCurrentLookupKey(),切換數(shù)據(jù)源時(shí)JDBC會(huì)調(diào)用這個(gè)方法獲取數(shù)據(jù)源的key,所以只需要實(shí)現(xiàn)該方法,改變?cè)摲椒ㄖ蟹祷氐膋ey值即可。

源碼解讀

1.從類關(guān)系圖中可以看出AbstractRoutingDataSource類實(shí)現(xiàn)了DataSource接口,后者有兩個(gè)getConnection()方法,即獲取DB連接的作用。

在這里插入圖片描述

2.AbstractRoutingDataSource實(shí)現(xiàn)了這兩個(gè)方法

在這里插入圖片描述

其中determineTargetDataSource()方法的作用就是獲取實(shí)際的數(shù)據(jù)源,其內(nèi)部調(diào)用了determineCurrentLookupKey()方法,取到當(dāng)前設(shè)定的key,通過(guò)key在上下文this.resolvedDataSources屬性中嘗試獲取DataSource對(duì)象,這個(gè)對(duì)象即當(dāng)前連接的數(shù)據(jù)源

在這里插入圖片描述

3.那么this.resolvedDataSources在哪里維護(hù)呢? 繼續(xù)在AbstractRoutingDataSource類里找,可以找到afterPropertiesSet()方法,這個(gè)方法是InitializingBean接口的,作用是在bean的所有屬性設(shè)置完成后便會(huì)調(diào)用此方法。可以看到this.resolvedDataSources是從this.targetDataSources取的信息。

在這里插入圖片描述

所以只需要改變this.targetDataSources,即可改變this.resolvedDataSources;后續(xù)改變determineCurrentLookupKey()的返回值(key),在調(diào)用getConnection()時(shí)即可獲取到指定的數(shù)據(jù)源

實(shí)現(xiàn)方式:注解+切面

別看步驟挺多,但其實(shí)很容易理解和使用

1.配置文件示例:

spring:
  datasource: 
    master: # 數(shù)據(jù)源master
      jdbc-url: jdbc:mysql://localhost:3306/master?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver
    db1: # 數(shù)據(jù)源1
      jdbc-url: jdbc:mysql://localhost:3306/db2?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8
      username: root
      password: root
      driver-class-name: com.mysql.cj.jdbc.Driver

2.創(chuàng)建數(shù)據(jù)源配置類

創(chuàng)建數(shù)據(jù)源配置類(我這里為了方便區(qū)分就為每一個(gè)數(shù)據(jù)源創(chuàng)建了一個(gè)配置類,當(dāng)然也可以把所有的數(shù)據(jù)源配置在一個(gè)類里)

@Configuration
@EnableConfigurationProperties({MasterDataSourceProperties.class})
public class MasterDataSourceConfig {
    /**
    * 這個(gè)MasterDataSourceProperties是讀取配置文件的類,我這里為了省篇幅就不展示了
    **/
    @Autowired
    private MasterDataSourceProperties masterDataSourceProperties;

    @Bean
    @Primary
    public DataSource masterDataSource() {
        DruidDataSource datasource = new DruidDataSource();
        datasource.setUrl(masterDataSourceProperties.getUrl());
        datasource.setUsername(masterDataSourceProperties.getUsername());
        datasource.setPassword(AESUtil.aesDecode(masterDataSourceProperties.getPassword()));
        datasource.setDriverClassName(masterDataSourceProperties.getDriverClassName());
        ......

        return datasource;
    }
}
@Configuration
@EnableConfigurationProperties({OdsDataSourceProperties.class})
public class DB1DataSourceConfig {
    /**
    * 這個(gè)DB1DataSourceProperties是讀取配置文件的類,我這里為了省篇幅就不展示了
    **/
    @Autowired
    private DB1DataSourceProperties dB1DataSourceProperties;

    @Bean
    public DataSource db1DataSource() {
        DruidDataSource datasource = new DruidDataSource();
        datasource.setUrl(dB1DataSourceProperties.getUrl());
        datasource.setUsername(dB1DataSourceProperties.getUsername());
        datasource.setPassword(AESUtil.aesDecode(dB1DataSourceProperties.getPassword()));
        datasource.setDriverClassName(dB1DataSourceProperties.getDriverClassName());
        ......

        return datasource;
    }
}

3.創(chuàng)建DynamicDataSource

創(chuàng)建自己的一個(gè)DynamicDataSource類(名字任意)繼承AbstractRoutingDataSource,維護(hù)數(shù)據(jù)源,提供切換方法。

public class DynamicDataSource extends AbstractRoutingDataSource {

    /**
    * 如果不希望數(shù)據(jù)源在啟動(dòng)配置時(shí)就加載好,可以定制這個(gè)方法,從任何你希望的地方讀取并返回?cái)?shù)據(jù)源
    * 比如從數(shù)據(jù)庫(kù)、文件、外部接口等讀取數(shù)據(jù)源信息,并最終返回一個(gè)DataSource實(shí)現(xiàn)類對(duì)象即可
    */
    @Override
    protected DataSource determineTargetDataSource() {
        return super.determineTargetDataSource();
    }

    /**
    * 如果希望所有數(shù)據(jù)源在啟動(dòng)配置時(shí)就加載好,然后通過(guò)設(shè)置數(shù)據(jù)源Key值來(lái)切換數(shù)據(jù),定制這個(gè)方法
    */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceKey();
    }

    /**
    * 設(shè)置默認(rèn)數(shù)據(jù)源
    *
    * @param defaultDataSource
    */
    public void setDefaultDataSource(Object defaultDataSource) {
        super.setDefaultTargetDataSource(defaultDataSource);
    }

    /**
    * 設(shè)置數(shù)據(jù)源
    *
    * @param dataSources
    */
    public void setDataSources(Map<Object, Object> dataSources) {
        super.setTargetDataSources(dataSources);
        // 將數(shù)據(jù)源的 key 放到數(shù)據(jù)源上下文的 key 集合中,用于切換時(shí)判斷數(shù)據(jù)源是否有效
        DynamicDataSourceContextHolder.addDataSourceKeys(dataSources.keySet());
    }
}

4.創(chuàng)建數(shù)據(jù)源上下文處理器DynamicDataSourceContextHolder

創(chuàng)建數(shù)據(jù)源上下文處理器DynamicDataSourceContextHolder用以存儲(chǔ)當(dāng)前線程需要使用的數(shù)據(jù)源名稱。

public class DynamicDataSourceContextHolder {

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
        /**
         * 將 master 數(shù)據(jù)源的 key作為默認(rèn)數(shù)據(jù)源的 key
         */
        @Override
        protected String initialValue() {
            return "master";
        }
    };


    /**
     * 數(shù)據(jù)源的 key集合,用于切換時(shí)判斷數(shù)據(jù)源是否存在
     */
    public static List<Object> dataSourceKeys = new ArrayList<>();

    /**
     * 切換數(shù)據(jù)源
     *
     * @param key
     */
    public static void setDataSourceKey(String key) {
        contextHolder.set(key);
    }

    /**
     * 獲取數(shù)據(jù)源
     *
     * @return
     */
    public static String getDataSourceKey() {
        return contextHolder.get();
    }

    /**
     * 重置數(shù)據(jù)源
     */
    public static void clearDataSourceKey() {
        contextHolder.remove();
    }

    /**
     * 判斷是否包含數(shù)據(jù)源
     *
     * @param key 數(shù)據(jù)源key
     * @return
     */
    public static boolean containDataSourceKey(String key) {
        return dataSourceKeys.contains(key);
    }

    /**
     * 添加數(shù)據(jù)源keys
     *
     * @param keys
     * @return
     */
    public static boolean addDataSourceKeys(Collection<? extends Object> keys) {
        return dataSourceKeys.addAll(keys);
    }
}

5.創(chuàng)建數(shù)據(jù)源配置類DataSourceConfig

創(chuàng)建數(shù)據(jù)源配置類DataSourceConfig,將所有數(shù)據(jù)源注入到spring容器

@Configuration
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class }) // 排除 DataSourceAutoConfiguration 的自動(dòng)配置,避免環(huán)形調(diào)用
public class DataSourceConfig {

    @Autowired
    private MasterDataSourceConfig masterDataSourceConfig;

    @Autowired
    private DB1DataSourceConfig dB1DataSourceConfig;
    /**
     * 設(shè)置動(dòng)態(tài)數(shù)據(jù)源為主數(shù)據(jù)源
     *
     * @return
     */
    @Bean
    @Primary
    public DynamicDataSource dataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 默認(rèn)指定的數(shù)據(jù)源
        dynamicDataSource.setDefaultDataSource(masterDataSourceConfig.masterDataSource());
        // 將數(shù)據(jù)源設(shè)置進(jìn)map
        Map<Object, Object> dataSourceMap = new HashMap<>(8);
        dataSourceMap.put(DataSourceEnum.MASTER.toString(), masterDataSourceConfig.masterDataSource());
        dataSourceMap.put(DataSourceEnum.DB1.toString(), dB1DataSourceConfig.db1DataSource());
        // 使用 Map 保存多個(gè)數(shù)據(jù)源,并設(shè)置到動(dòng)態(tài)數(shù)據(jù)源對(duì)象中,這個(gè)值最終會(huì)在afterPropertiesSet中被設(shè)置到resolvedDataSources上
        dynamicDataSource.setDataSources(dataSourceMap);
        return dynamicDataSource;
    }
}

6.創(chuàng)建數(shù)據(jù)源類型枚舉DataSourceEnum

public enum DataSourceEnum {

    /**默認(rèn)類型*/
    MASTER,
    /**DB1類型*/
    DB1,
    ;
}

7.創(chuàng)建自定義注解@DataSource

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {

    /**
     * 數(shù)據(jù)源key值
     * @return
     */
    DataSourceEnum value();
}

8.創(chuàng)建切面DynamicDataSourceAspect

@Slf4j
@Aspect
@Order(-1)
@Component
public class DynamicDataSourceAspect {

    /**
     * 切換數(shù)據(jù)源
     *
     * @param point
     * @param dataSource
     */
    @Before("@annotation(dataSource))")
    public void switchDataSource(JoinPoint point, DataSource dataSource) {
        if (!DynamicDataSourceContextHolder.containDataSourceKey(dataSource.value().toString())) {
            log.info("DataSource [{}] doesn't exist, use default DataSource", dataSource.value());
        } else {
            // 切換數(shù)據(jù)源
            DynamicDataSourceContextHolder.setDataSourceKey(dataSource.value().toString());
            log.info("Switch DataSource to [{}] in Method [{}]", DynamicDataSourceContextHolder.getDataSourceKey(),
                    point.getSignature());
        }
    }

    /**
     * 重置數(shù)據(jù)源
     *
     * @param point
     * @param dataSource
     */
    @After("@annotation(dataSource))")
    public void restoreDataSource(JoinPoint point, DataSource dataSource) {
        // 將數(shù)據(jù)源置為默認(rèn)數(shù)據(jù)源
        DynamicDataSourceContextHolder.clearDataSourceKey();
        log.info("Restore DataSource to [{}] in Method [{}]", DynamicDataSourceContextHolder.getDataSourceKey(), point.getSignature());
    }
}

如何使用

@Override
@DataSource(DataSourceEnum.DB1)
public Page<AuditTaskDto> queryAuditTask(AuditTaskQuery query) {
    Page<AuditTaskDto> page = baseMapper.queryAuditTask(query);
    return page;
}

如此就可以直接使用自定義的@DataSource注解來(lái)切換數(shù)據(jù)源啦~~~

以上就是SpringBoot配置動(dòng)態(tài)數(shù)據(jù)源的實(shí)戰(zhàn)詳解的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot動(dòng)態(tài)數(shù)據(jù)源的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 使用springBoot中的info等級(jí)通過(guò)druid打印sql

    使用springBoot中的info等級(jí)通過(guò)druid打印sql

    這篇文章主要介紹了使用springBoot中的info等級(jí)通過(guò)druid打印sql,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • SpringBoot實(shí)現(xiàn)類似鉤子函數(shù)的方法

    SpringBoot實(shí)現(xiàn)類似鉤子函數(shù)的方法

    這篇文章主要給大家介紹了關(guān)于SpringBoot實(shí)現(xiàn)類似鉤子函數(shù)的方法,文中通過(guò)代碼示例介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2024-04-04
  • springboot jasypt2.x與jasypt3.x的使用方式

    springboot jasypt2.x與jasypt3.x的使用方式

    在軟件開發(fā)中,將配置文件中的敏感信息(如數(shù)據(jù)庫(kù)密碼)進(jìn)行加密是保障安全的有效手段,jasypt框架提供了這一功能,支持通過(guò)加密工具類或命令行工具生成密文,并通過(guò)修改配置文件和啟動(dòng)參數(shù)的方式使用密文和密鑰,這樣即便配置文件被泄露
    2024-09-09
  • MyBatis-Plus中的邏輯刪除功能及實(shí)例分析

    MyBatis-Plus中的邏輯刪除功能及實(shí)例分析

    本文將詳細(xì)講解MyBatis-Plus中的邏輯刪除特性,并結(jié)合實(shí)際案例進(jìn)行演示和說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • java線程池工作隊(duì)列飽和策略代碼示例

    java線程池工作隊(duì)列飽和策略代碼示例

    這篇文章主要介紹了java線程池工作隊(duì)列飽和策略代碼示例,涉及線程池的簡(jiǎn)單介紹,工作隊(duì)列飽和策略的分析及代碼示例,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • Mybatis配置之typeAlias標(biāo)簽的用法

    Mybatis配置之typeAlias標(biāo)簽的用法

    這篇文章主要介紹了Mybatis配置之typeAlias標(biāo)簽的用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Java游戲服務(wù)器之?dāng)?shù)據(jù)庫(kù)表存取封裝

    Java游戲服務(wù)器之?dāng)?shù)據(jù)庫(kù)表存取封裝

    這篇文章主要介紹了Java游戲服務(wù)器之?dāng)?shù)據(jù)庫(kù)表存取封裝的相關(guān)資料,需要的朋友可以參考下
    2015-11-11
  • 淺析JPA分類表的操作函數(shù)

    淺析JPA分類表的操作函數(shù)

    這篇文章主要介紹了JPA分類表的操作函數(shù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2023-02-02
  • java學(xué)生信息管理系統(tǒng)源代碼

    java學(xué)生信息管理系統(tǒng)源代碼

    這篇文章主要為大家詳細(xì)介紹了java學(xué)生信息管理系統(tǒng)源代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • springboot如何使用thymeleaf模板訪問(wèn)html頁(yè)面

    springboot如何使用thymeleaf模板訪問(wèn)html頁(yè)面

    springboot中推薦使用thymeleaf模板,使用html作為頁(yè)面展示。那么如何通過(guò)Controller來(lái)訪問(wèn)來(lái)訪問(wèn)html頁(yè)面呢?下面通過(guò)本文給大家詳細(xì)介紹,感興趣的朋友跟隨腳本之家小編一起看看吧
    2018-05-05

最新評(píng)論