美文网首页
解析配置文件自动装配 DataSource + Abstract

解析配置文件自动装配 DataSource + Abstract

作者: DJN_ | 来源:发表于2018-11-13 16:30 被阅读0次

    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 有比较大的变动,可参考这里

    相关文章

      网友评论

          本文标题:解析配置文件自动装配 DataSource + Abstract

          本文链接:https://www.haomeiwen.com/subject/taywxqtx.html