当前位置: 首页 > news >正文

3.3 Spring Boot多数据源动态切换:AbstractRoutingDataSource实战

Spring Boot多数据源动态切换:AbstractRoutingDataSource实战

引言

在企业级应用开发中,随着业务规模的扩大,我们经常会遇到需要同时操作多个数据库的场景:可能是主从读写分离、多租户架构、分库分表需求,或是需要同时连接业务库和日志库等特殊场景。传统的单数据源配置已无法满足这类需求,而Spring Boot提供的AbstractRoutingDataSource正是解决这类问题的利器。本文将深入探讨如何基于AbstractRoutingDataSource实现多数据源的动态切换,并通过实战代码演示具体实现方案。


一、核心原理剖析

1.1 AbstractRoutingDataSource工作机制

AbstractRoutingDataSource是Spring框架提供的一个抽象类,通过路由机制(Routing)实现数据源的动态切换。其核心逻辑是维护一个Map<Object, DataSource>结构,通过determineCurrentLookupKey()方法返回当前线程需要使用的数据源标识(lookup key),进而从目标数据源集合中获取对应的DataSource

1.2 线程级数据源管理

动态数据源切换的核心是线程隔离。每个请求线程通过ThreadLocal保存当前数据源标识,确保不同线程间的数据源选择互不干扰。这种设计完美适配Web应用的请求-响应模型,天然支持高并发场景。


二、实战:四步实现动态数据源

2.1 环境准备

pom.xml中添加基础依赖:

 

xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

2.2 数据源配置

application.yml中配置主从数据源:

 

yaml

datasource:
  master:
    driver-class-name: com.mysql.cj.jdbc.Driver
    jdbc-url: jdbc:mysql://localhost:3306/master_db
    username: root
    password: master_pwd
    pool-name: MASTER_POOL
  slave:
    driver-class-name: com.mysql.cj.jdbc.Driver
    jdbc-url: jdbc:mysql://localhost:3306/slave_db
    username: root
    password: slave_pwd
    pool-name: SLAVE_POOL

2.3 动态数据源核心实现

2.3.1 数据源上下文持有器
 

java

public class DataSourceContextHolder {
    private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();

    public static void setDataSource(String name) {
        CONTEXT.set(name);
    }

    public static String getDataSource() {
        return CONTEXT.get();
    }

    public static void clear() {
        CONTEXT.remove();
    }
}
2.3.2 动态数据源实现类
 

java

public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }

    @Bean
    @ConfigurationProperties(prefix = "datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties(prefix = "datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean
    public DataSource dynamicDataSource() {
        Map<Object, Object> dataSources = new HashMap<>(2);
        dataSources.put("master", masterDataSource());
        dataSources.put("slave", slaveDataSource());

        DynamicDataSource ds = new DynamicDataSource();
        ds.setDefaultTargetDataSource(masterDataSource());
        ds.setTargetDataSources(dataSources);
        return ds;
    }
}

2.4 切换切面实现

 

java

@Aspect
@Component
public class DataSourceAspect {

    @Before("@annotation(com.example.annotation.Master)")
    public void switchMaster() {
        DataSourceContextHolder.setDataSource("master");
    }

    @Before("@annotation(com.example.annotation.Slave)")
    public void switchSlave() {
        DataSourceContextHolder.setDataSource("slave");
    }

    @AfterReturning("@annotation(com.example.annotation.Master) || @annotation(com.example.annotation.Slave)")
    public void clearDataSource() {
        DataSourceContextHolder.clear();
    }
}

三、进阶功能扩展

3.1 动态数据源注册

 

java

public void addNewDataSource(String key, DataSource dataSource) {
    Map<Object, Object> targetDataSources = 
        new HashMap<>(dynamicDataSource.getTargetDataSources());
    targetDataSources.put(key, dataSource);
    dynamicDataSource.setTargetDataSources(targetDataSources);
    dynamicDataSource.afterPropertiesSet();
}

3.2 事务管理增强

 

java

@Bean
public PlatformTransactionManager transactionManager() {
    return new DataSourceTransactionManager(dynamicDataSource());
}

四、测试验证

 

java

@SpringBootTest
class DynamicDSTest {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Test
    @Master
    void testMasterWrite() {
        jdbcTemplate.execute("INSERT INTO user(name) VALUES('test')");
    }

    @Test
    @Slave
    void testSlaveRead() {
        List<Map<String, Object>> result = 
            jdbcTemplate.queryForList("SELECT * FROM user");
        System.out.println(result);
    }
}

五、注意事项与优化建议

  1. 连接池监控:建议集成Druid的监控功能,实时观察各数据源状态
  2. 失败回退机制:当目标数据源不可用时,自动切换到默认数据源
  3. 性能调优:根据实际场景调整各连接池参数(maxPoolSize、minIdle等)
  4. 分布式事务:对于跨数据源事务,建议使用Seata等分布式事务解决方案
  5. 动态刷新:结合Nacos配置中心实现数据源配置的热更新

总结

通过AbstractRoutingDataSource实现多数据源动态切换,我们既保持了Spring Boot简洁的配置风格,又获得了灵活的数据源管理能力。这种方案在中小型项目中表现优异,但对于需要复杂分片策略的大型分布式系统,建议考虑集成ShardingSphere等专业中间件。希望本文能为您在应对复杂数据源场景时提供有价值的参考。

相关文章:

  • 工作记录 2017-01-13
  • odbus TCP转Modbus RTU网关快速配置案例
  • uniapp-x 子组件样式覆盖
  • [笔记.AI]数据集——大模型的“教科书” | 数据集的细分、作用和意义
  • 高德地图猎鹰服务调用指南(Java后端)
  • postman通过json获取接口返回token,设置为全局变量
  • Unity插件-适用于画面传输的FMETP STREAM使用方法(一)FMETP STREAM介绍
  • 安全相关Python脚本
  • 【leetcode hot 100 108】将有序数组转换为二叉搜索树
  • 工厂方法模式 (Factory Method Pattern)
  • 人工智能之数学基础:保持几何结构不变的线性变换——正交变换
  • 查找Python环境中Matplotlib配置文件
  • 计算机的结构形式
  • 《Flutter:开源的跨平台移动应用开发框架》:此文为AI自动生成
  • 激活函数和批归一化(BatchNorm)
  • 数位小游戏
  • Vue生命周期_Vue生命周期钩子
  • 使用SetupTools 管理你的项目打包工作
  • 【后端开发面试题】每日 3 题(十三)
  • redis删除与先判断再删除的区别
  • 国税总局上海市税务局回应刘晓庆被举报涉嫌偷漏税:正依法依规办理
  • 杞支雅男评《1517》|放眼世界,立足德国
  • 上海静安将发放七轮文旅消费券,住宿券最高满800元减250元
  • 从《让·桑特伊》到《追忆》,假故事的胜利
  • 人民日报评外卖平台被约谈:合法规范经营,公平有序竞争
  • 西北大学副校长范代娣成陕西首富?系家庭财富,本人已从上市公司退出