2. 如何在 SpingBoot中 使用 多个数据源

因为有了auto-configure , 我们在 SpringBoot 的 配置得到了极大的简化 , 比如数据源 , 我们只需要在 application.properties 中进行声明相关的参数就可以 , 但有一些场景 , 我们需要加载多个数据源 , 这里简单介绍一下多数据源的配置

创建数据源配置

@Configuration
@PropertySource(encoding = "utf-8" , value = "file:/fileName.properties")
public class JobDataSourceConfiguration {
    // 标记为主数据源
    @Primary
    @Bean("tradeDataSource")
    @ConfigurationProperties(value = "datasource.trade")
    public DataSource tradeDataSource(){
        return DataSourceBuilder.create().build();
    }

    @Bean("paymentDataSource")
    @ConfigurationProperties(value = "datasource.payment")
    public DataSource paymentDataSource(){
        return DataSourceBuilder.create().build();
    }

    @Bean("productDataSource")
    @ConfigurationProperties(value = "datasource.product")
    public DataSource productDataSource(){
        return DataSourceBuilder.create().build();
    }

}

配置文件

#trade
datasource.trade.jdbc-url=
datasource.trade.username=
datasource.trade.password=
datasource.trade.driver-class-name=com.mysql.jdbc.Driver
datasource.trade.type=com.zaxxer.hikari.HikariDataSource
datasource.trade.hikari.maximum-pool-size=
datasource.trade.hikari.minimum-idle=
datasource.trade.hikari.idle-timeout=

#info
datasource.product.jdbc-url=
datasource.product.username=
datasource.product.password=
datasource.product.driver-class-name=com.mysql.jdbc.Driver
datasource.product.type=com.zaxxer.hikari.HikariDataSource
datasource.product.hikari.maximum-pool-size=
datasource.product.hikari.minimum-idle=
datasource.product.hikari.idle-timeout=

#payment
datasource.payment.jdbc-url=
datasource.payment.username=
datasource.payment.password=
datasource.payment.driver-class-name=com.mysql.jdbc.Driver
datasource.payment.type=com.zaxxer.hikari.HikariDataSource
datasource.payment.hikari.maximum-pool-size=
datasource.payment.hikari.minimum-idle=
datasource.payment.hikari.idle-timeout=

如何使用

在上面 , 我们配置了三个数据源 , 并且标记了tradeDataSource为默认数据源

如果我们程序中 , 有使用Jpa 和 autoconfigure的话 , 那么默认的数据源 会自己注入 .

如果使用 JdbcTemplate 可参考下述方式

@Configuration
public class JdbcTemplateConfiguration {

    @Bean("tradeJdbcTemplate")
    public JdbcTemplate tradeJdbcTemplate(@Qualifier("tradeDataSource") DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }

    @Bean("productJdbcTemplate")
    public JdbcTemplate productJdbcTemplate(@Qualifier("productDataSource") DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }

    @Bean("paymentJdbcTemplate")
    public JdbcTemplate paymentJdbcTemplate(@Qualifier("paymentDataSource") DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }
}

DataSourceBuilder 是如何帮助我们创建数据源的 ?
通过上面的方式 , 我们已经可以创建多个数据源并开始使用了
但可能有些同学 还比较好奇 DataSourceBuilder究竟做了什么
下面我们分析一下它的源码

public class DataSourceBuilder {
    // 支持的数据源池
    private static final String[] DATA_SOURCE_TYPE_NAMES = new String[]{"org.apache.tomcat.jdbc.pool.DataSource", "com.zaxxer.hikari.HikariDataSource", "org.apache.commons.dbcp.BasicDataSource", "org.apache.commons.dbcp2.BasicDataSource"};
    private Class type;
    private ClassLoader classLoader;
    private Map properties = new HashMap();

    // 1. 实例化 
    public static DataSourceBuilder create() {
        return new DataSourceBuilder((ClassLoader)null);
    }
    // 1. 实例化 , 并且传入ClassLoader

    public static DataSourceBuilder create(ClassLoader classLoader) {
        return new DataSourceBuilder(classLoader);
    }

    public DataSourceBuilder(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }
    // 2. 创建数据源
    public DataSource build() {
        // 3. 获取数据源类型
        Class type = this.getType();
        // 5. 实例化数据源
        DataSource result = (DataSource)BeanUtils.instantiate(type);
        // 6. 尝试获取驱动类名
        this.maybeGetDriverClassName();
        // 7. 在数据源中 , 绑定配置中的参数
        this.bind(result);
        return result;
    }

    private void maybeGetDriverClassName() {
        // 如果实例中没有直接声明驱动类 , 并且实例中声明了数据库连接的地址
        if(!this.properties.containsKey("driverClassName") && this.properties.containsKey("url")) {
            String url = (String)this.properties.get("url");
            // 根据 连接地址 解析驱动类名
            String driverClass = DatabaseDriver.fromJdbcUrl(url).getDriverClassName();
            this.properties.put("driverClassName", driverClass);
        }

    }
    // 在数据源中 , 绑定配置中的参数
    private void bind(DataSource result) {
        MutablePropertyValues properties = new MutablePropertyValues(this.properties);
        (new RelaxedDataBinder(result)).withAlias("url", new String[]{"jdbcUrl"}).withAlias("username", new String[]{"user"}).bind(properties);
    }

    // 显示的传入 url
    public DataSourceBuilder type(Class type) {
        this.type = type;
        return this;
    }
    // 显示的传入 url
    public DataSourceBuilder url(String url) {
        this.properties.put("url", url);
        return this;
    }
    // 显示的传入 驱动类名
    public DataSourceBuilder driverClassName(String driverClassName) {
        this.properties.put("driverClassName", driverClassName);
        return this;
    }
    // 显示的传入 用户名
    public DataSourceBuilder username(String username) {
        this.properties.put("username", username);
        return this;
    }
    // 显示的传入 密码
    public DataSourceBuilder password(String password) {
        this.properties.put("password", password);
        return this;
    }
    // 4. 查找数据源的具体类型
    public Class findType() {
        // 如果实例中, 已经存在数据源类型了 , 那么直接返回
        if(this.type != null) {
            return this.type;
        } else {
            String[] var1 = DATA_SOURCE_TYPE_NAMES;
            int var2 = var1.length;
            int var3 = 0;
            // 支持的数据源类型
            while(var3 < var2) {
                String name = var1[var3];
                // 查找数据源的类 , 如果在查找不到的话 , 移动下标, 查找下一个数据源
                try {
                    return ClassUtils.forName(name, this.classLoader);
                } catch (Exception var6) {
                    ++var3;
                }
            }

            return null;
        }
    }

    // 3. 获取数据源类型
    private Class getType() {
        Class type = this.findType();
        if(type != null) {
            return type;
        } else {
            throw new IllegalStateException("No supported DataSource type found");
        }
    }
}

你可能感兴趣的:(2. 如何在 SpingBoot中 使用 多个数据源)