SpringBoot动态配置数据源的三种实现方式

 更新时间:2025年08月13日 10:17:11   作者:Best_Liu~  
本文介绍了Spring Boot动态数据源的三种实现方式:通过配置文件设置数据源,自定义DynamicDataSource继承AbstractRoutingDataSource实现动态切换,利用ThreadLocal管理数据源标识,并结合AOP控制切换逻辑,同时需整合MyBatis-Plus配置,需要的朋友可以参考下

一、Apache Druid方式

1、配置文件

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    master:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&generateSimpleParameterMetadata=true&autoReconnect=true&useSSL=true
      username: root
      password: root3306
    slave:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&generateSimpleParameterMetadata=true&autoReconnect=true&useSSL=true
      username: root
      password: root3306
    druid-pool:
      #连接池的最大数据库连接数。设为0表示无限制
      max-active: 20
      #初始化数量
      initial-size: 2
      #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制
      max-wait: 60000
      #最小空闲连接:连接池中容许保持空闲状态的最小连接数量,低于这个数量将创建新的连接
      min-idle: 2

2、配置数据源

配置数据源基本信息,并将所有数据源添加到动态数据源中进行进行管理

@Configuration
public class DynamicDataSourceConfig {
 
    @Value("${spring.datasource.type}")
    private Class<? extends DataSource> dataSourceType;
 
    @Value("${spring.datasource.druid-pool.initial-size}")
    private int initialSize;
    @Value("${spring.datasource.druid-pool.max-active}")
    private int maxActive;
    @Value("${spring.datasource.druid-pool.max-wait}")
    private int maxWait;
    @Value("${spring.datasource.druid-pool.min-idle}")
    private int minIdle;
 
    @Primary
    @Bean("masterDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource masterDataSource() {
        DruidDataSource druidDataSource = (DruidDataSource) DataSourceBuilder.create().type(dataSourceType).build();
        druidPoolConfig(druidDataSource);
        return druidDataSource;
    }
 
    @Bean("slaveDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDataSource() {
        DruidDataSource druidDataSource = (DruidDataSource) DataSourceBuilder.create().type(dataSourceType).build();
        druidPoolConfig(druidDataSource);
        return druidDataSource;
    }
 
    private void druidPoolConfig(DruidDataSource druidDataSource) {
        druidDataSource.setMaxActive(maxActive);
        druidDataSource.setMaxWait(maxWait);
        druidDataSource.setInitialSize(initialSize);
        druidDataSource.setMinIdle(minIdle);
    }
 
    @Bean("dataSource")
    @DependsOn({"masterDataSource", "slaveDataSource"})
    public DataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource, @Qualifier("slaveDataSource") DataSource slaveDataSource) {
        Map<Object, Object> dataSourceMap = new HashMap<>();
        dataSourceMap.put("masterDataSource", masterDataSource);
        dataSourceMap.put("slaveDataSource", slaveDataSource);
        DynamicDataSourceContextHolder.dataSourceNames.add("masterDataSource");
        DynamicDataSourceContextHolder.dataSourceNames.add("slaveDataSource");
        // 设置动态数据源
        return new DynamicDataSource(masterDataSource, dataSourceMap);
    }
}
public class DynamicDataSource extends AbstractRoutingDataSource {
 
    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
    }
 
    @Override
    public Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSource();
    }
}

自定义DynamicDataSouce并继承AbstractRoutingDataSource 。AbstractRoutingDataSource 是 Spring 框架中用于实现动态数据源路由的一个抽象类。它通过实现数据源的动态切换来支持多数据源的应用场景。

AbstractRoutingDataSource 的核心原理是通过一个线程本地变量(ThreadLocal)来保存当前使用的数据源标识,然后在每次获取连接时,根据这个标识来决定使用哪个数据源。

主要方法包括:

1)determineCurrentLookupKey():这是一个抽象方法,需要子类实现。它用于确定当前使用的数据源标识。根据返回的标识,AbstractRoutingDataSource 会从预先配置的数据源中选择对应的数据源。

2)setTargetDataSources():用于设置数据源的映射关系。这个方法接受一个 Map<Object, Object> 类型的参数,键为数据源标识,值为具体的数据源对象。

3)setDefaultTargetDataSource():用于设置默认的数据源。如果没有找到匹配的数据源标识,就会使用默认的数据源。

4)afterPropertiesSet():检查配置,确保 targetDataSources 和 defaultTargetDataSource 属性已被设置;初始化映射,将指定的默认数据源和目标数据源映射解析并存储到相应的内部变量中。

public class DynamicDataSourceContextHolder {
    private static final ThreadLocal<String> DATASOURCE_CONTEXT_HOLDER = new ThreadLocal<>();
    /**
     * 管理全部数据源
     */
    public static List<String> dataSourceNames = new ArrayList<>();
 
    /**
     * 判断是否存在指定数据源
     */
    public static boolean containsDataSource(String dataSourceName) {
        return dataSourceNames.contains(dataSourceName);
    }
    /**
     * 设置当前数据源
     */
    public static void setDataSource(String dataSourceName) {
        DATASOURCE_CONTEXT_HOLDER.set(dataSourceName);
    }
    /**
     * 获取当前数据源
     */
    public static String getDataSource() {
        return DATASOURCE_CONTEXT_HOLDER.get();
    }
    /**
     * 清除数据源
     */
    public static void clear() {
        DATASOURCE_CONTEXT_HOLDER.remove();
    }
}

ThreadLocal是Java中的一个工具类,能够为每个线程提供一个独立的变量副本,从而实现线程隔离。利用ThreadLocal管理当前使用的数据源,DynamicDataSourceContextHolder可以在不同的线程间隔离数据源的使用,实现数据源的动态切换。

如果整合使用了Mybatis-Plus,则还需要添加Mybatis-Plus的相关配置,否则会报错,如下所示

@Configuration
@MapperScan(basePackages = {"com.practice.mapper"}, sqlSessionTemplateRef = "sqlTemplateDefault")
public class MybatisConfig {
    @Bean(name = "sqlFactoryDefault")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource)
            throws Exception {
        MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
        factoryBean.setDataSource(dynamicDataSource);
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        factoryBean.setMapperLocations(resolver.getResources("classpath*:mapper/*/*.xml"));
        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();
        mybatisConfiguration.setLogImpl(StdOutImpl.class);
        factoryBean.setConfiguration(mybatisConfiguration);
        return factoryBean.getObject();
    }
 
    @Bean(name = "sqlTemplateDefault")
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlFactoryDefault") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

3、利用AOP动态切换

自定义数据源标识注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DS {
    String value() default "masterDataSource";
}

利用AOP实现动态切换数据源

@Component
@Aspect
@Slf4j
public class DynamicDataSourceAspect {
    private static final String DEFAULT_DATASOURCE = "masterDataSource";
 
    @Pointcut("@annotation(com.practice.annotation.DS)")
    public void dsPointCut() {}
 
    @Before("dsPointCut() && @annotation(ds)")
    public void changeDataSource(JoinPoint joinPoint, DS ds) {
        String dataSourceName = ds.value();
        if (DynamicDataSourceContextHolder.containsDataSource(dataSourceName)) {
            DynamicDataSourceContextHolder.setDataSource(dataSourceName);
            log.info("切换到数据源:{}", dataSourceName);
        } else {
            log.error("数据源不存在:{}", dataSourceName);
        }
    }
 
    @After("@annotation(ds)")
    public void clearDataSource(JoinPoint joinPoint, DS ds) {
        DynamicDataSourceContextHolder.clear();
    }
}

以上就是SpringBoot动态配置数据源的三种实现方式的详细内容,更多关于SpringBoot动态配置数据源的资料请关注脚本之家其它相关文章!

相关文章

  • 解决JAVA遍历List集合,删除数据时出现的问题

    解决JAVA遍历List集合,删除数据时出现的问题

    这篇文章主要介绍了解决JAVA遍历List集合时,删除数据出现的问题,文中讲解非常细致,帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-07-07
  • MyBatis3传递多个参数(Multiple Parameters)

    MyBatis3传递多个参数(Multiple Parameters)

    这篇文章主要介绍了MyBatis3传递多个参数,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • Java基础知识杂文

    Java基础知识杂文

    这篇文章主要介绍了Java基础知识杂文,具有一定借鉴价值,需要的朋友可以参考下。
    2017-12-12
  • Java代理模式实例详解【静态代理与动态代理】

    Java代理模式实例详解【静态代理与动态代理】

    这篇文章主要介绍了Java代理模式,结合实例形式详细分析了java静态代理与动态代理模式相关概念、原理、操作技巧与注意事项,需要的朋友可以参考下
    2019-09-09
  • Java MyBatis实战之QueryWrapper中and和or拼接技巧大全

    Java MyBatis实战之QueryWrapper中and和or拼接技巧大全

    在Java中QueryWrapper是MyBatis-Plus框架中的一个查询构造器,它提供了丰富的查询方法,其中包括and和or方法,可以用于构建复杂的查询条件,这篇文章主要给大家介绍了关于Java MyBatis实战之QueryWrapper中and和or拼接技巧的相关资料,需要的朋友可以参考下
    2024-07-07
  • 一步步教你写一个SpringMVC框架

    一步步教你写一个SpringMVC框架

    现在主流的Web MVC框架除了Struts这个主力外,其次就是Spring MVC了,因此这也是作为一名程序员需要掌握的主流框架,这篇文章主要给大家介绍了关于如何一步步写一个SpringMVC框架的相关资料,需要的朋友可以参考下
    2022-03-03
  • java程序运行时内存分配详解

    java程序运行时内存分配详解

    这篇文章主要介绍了java程序运行时内存分配详解 ,需要的朋友可以参考下
    2016-07-07
  • SpringBoot3.3.0升级方案

    SpringBoot3.3.0升级方案

    本文介绍了由SpringBoot2升级到SpringBoot3.3.0升级方案,新版本的升级可以解决旧版本存在的部分漏洞问题,感兴趣的可以了解一下
    2024-08-08
  • springboot统一异常处理(返回json)并格式化异常

    springboot统一异常处理(返回json)并格式化异常

    这篇文章主要介绍了springboot统一异常处理(返回json)并格式化异常,对spring boot的默认异常处理方式进行修改,要统一返回数据格式,优雅的数据交互,优雅的开发应用,需要的朋友可以参考下
    2023-07-07
  • 分析SpringBoot的启动原理

    分析SpringBoot的启动原理

    这篇文章主要分析了SpringBoot的启动原理,帮助大家更好的理解和使用spring boot框架,感兴趣的朋友可以了解下
    2020-09-09

最新评论