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

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等专业中间件。希望本文能为您在应对复杂数据源场景时提供有价值的参考。

原文地址:https://blog.csdn.net/u012762619/article/details/146174302
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.kler.cn/a/587819.html

相关文章:

  • 软件环境安装-通过Docker安装Elasticsearch和Kibana【保姆级教程、内含图解】
  • 关于深度学习参数寻优的一些介绍
  • Tcp网络通信的基本流程梳理
  • 当今前沿技术:人工智能与区块链的未来发展
  • 科大讯飞嵌入式软件开发面试总结
  • Vue与Django是如何传递参数的?
  • python-53-分别使用flask和streamlit进行向量存储和检索的服务开发实战
  • C语言中的指针与函数
  • 【PyMySQL】Python操作MySQL
  • 利用Python爬虫根据关键词获取商品列表
  • OpenHarmony 5.0 MP4封装的H265视频播放失败的解决方案
  • idea 2023社区版自动生成 serialVersionUID
  • 洛谷P11043
  • Redisson 分布式锁全面解析:锁类型(可重入锁、公平锁、联锁、红锁、读写锁)和锁常见方法解读
  • redis删除与先判断再删除的区别
  • deepseek+kimi做ppt教程记录
  • VSCode通过SSH远程登录Windows服务器
  • docker网络问题导致dify添加API不能成功,如添加SearXNG
  • 人工智能之数学基础:如何将线性变换转换为矩阵?
  • 【视频】V4L2、ffmpeg、OpenCV中对YUV的定义