1. 程式人生 > >解析配置檔案自動裝配 DataSource + AbstractRoutingDataSource + AOP 實現動態資料來源 上:原理解析,解析資料來源

解析配置檔案自動裝配 DataSource + AbstractRoutingDataSource + AOP 實現動態資料來源 上:原理解析,解析資料來源

spring boot 自動裝配會通過 spring.datasource.*為我們自動裝配資料來源,所以想要動態的切換資料來源,第一件事是配置資料來源,其次是怎麼切換?最後何時切換?

原理解析(使用 AbstractRoutingDataSource 實現)

spring-jdbc 提供了 AbstractRoutingDataSourcegetConnection() 時通過 lookup key決定目標資料來源,使用 AbstractRoutingDataSource 需要準備至少兩個資料來源,這在原始碼中也有體現:

一個預設資料來源 + 動態匹配的資料來源,resolvedDataSources

是一個 Object 為 key,DataSource為 value 的 Map,可見這裡 key 即充當了 lookup key的角色。
image.png
在呼叫 getConnection 獲取資料來源時會呼叫 determineTargetDataSource 方法獲取目標資料來源,進而通過 determineCurrentLookupKey 方法獲得當前的 lookup key,再從 resolvedDataSources 中獲得目標資料來源。

AbstractRoutingDataSource 是一個抽象類,determineCurrentLookupKey是其唯一的抽象方法,意味著子類只需在適當的時候修改當前的 lookup key

就能實現動態的更改當前的資料來源。

配置資料來源

配置資料來源也就是給 AbstractRoutingDataSourcedefaultTargetDataSourceresolvedDataSources 進行賦值。

這裡我們將資料來源配置在 properties 檔案中,通過工具類解析進行配置。

在 properties 中配置資料來源

demo 專案為 spring boot 專案,配置檔案採用 yml 格式:

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/db?useUnicode=true&characterEncoding
=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false username: root password: 1234 driver-class-name: com.mysql.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource multi: ds-keys: db1,db2 db1: url: jdbc:mysql://127.0.0.1:3306/db11?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false username: root password: 1234 driver-class-name: com.mysql.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource db2: url: jdbc:mysql://127.0.0.1:3306/db12?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false username: root password: 1234 driver-class-name: com.mysql.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource

spring.datasource 字首的將被解析為預設資料來源, spring.datasource.multi 字首的則作為動態資料來源進行解析。
spring.datasource.multi.ds-keys 是比較關鍵的屬性,這個引數定義了專案中所有的動態資料來源的 key,如上述程式碼所示,spring.datasource.multi.ds-keys: db1,db2:表明共有兩個動態資料來源,key 分別為 db1 和 db2 ,進行解析時將解析如下兩個字首配置的資料來源:

  • spring.datasource.multi.db1
  • spring.datasource.multi.db2

當然這個規則是我自己定義的,可以按需定義自己的規則並實現相應的解析。

解析資料來源

使用 DynamicDataSourceBuilder 類來解析資料來源,註冊為 bean 並實現 EnvironmentAware 介面,在 setEnvironment 方法中開始進行解析。
image.png

initDefaultDataSourceinitCustomDataSources 方法將解析得到預設資料來源和動態資料來源,預設資料來源將賦值給 defaultDataSourcetargetDataSources 中是所有解析得到的資料來源,包括預設資料來源,其資料型別是一個 Map,Map 的 key 即為 Lookup key

初始化動態資料來源

    /**
     * 初始化定製資料來源
     */
    private void initCustomDataSources(Environment env) {

        // 讀取配置檔案獲取定製資料來源,也可以通過資料庫獲取資料來源
        String dsNames = env.getProperty(customDataSourceKeys);

        for (String dsKey : dsNames.split(",")) {
            DataSource ds = buildDataSource(env, customDataSourcePrefix + "." + dsKey);
            targetDataSources.put(dsKey, ds);
            dataBinder(ds, env);
        }

    }

customDataSourceKeys 即為 spring.datasource.multi.ds-keys,得到定製資料來源字首後進行構建,之後新增到 targetDataSources 中。

構建資料來源


    /**
     * 建立 datasource.
     */
    @SuppressWarnings("unchecked")
    private DataSource buildDataSource(Environment env, String dsPrefix) {

        try {

            String prefix = dsPrefix + ".";
            String dbpType = env.getProperty(prefix + DataSourcePropertyKey.type, defaultDataSourceType);
            Class<? extends DataSource> dsType = (Class<? extends DataSource>) Class.forName(dbpType);

            DataSource dataSource = DataSourceBuilder.create()
                    .driverClassName(env.getProperty(prefix + DataSourcePropertyKey.driverClassName))
                    .url(env.getProperty(prefix + DataSourcePropertyKey.url))
                    .username(env.getProperty(prefix + DataSourcePropertyKey.username))
                    .password(env.getProperty(prefix + DataSourcePropertyKey.password))
                    .type(dsType)
                    .build();

            configDataSourcePool(dataSource);
            return dataSource;

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

    }

資料來源的構建通過讀取 properties 檔案,並藉助 DataSourceBuilder 類進行構建。configDataSourcePool 方法可對資料來源對應的連線池進行配置。

這裡需要注意的是 spring boot 2.* 對配置檔案的讀取 API 有比較大的變動,可參考這裡