☰
SpringBoot多数据源配置,其实没你想的那么难
2026/10/4 21:11:06 网站建设 项目流程

很多后端开发一听到“多数据源”就头大,觉得要写一堆配置、切面、注解,还容易踩坑。其实,SpringBoot多数据源的核心就一句话:动态路由 + 多个DataSource Bean。搞懂这个,半小时就能跑起来。

为什么需要多数据源?

常见场景有三个:读写分离(主库写、从库读)、分库分表(不同业务用不同库)、老系统迁移(新老数据库并存)。不管哪种,本质都是让不同的SQL请求落到不同的数据库连接上。SpringBoot默认只配一个DataSource,多数据源就是要打破这个默认。

核心思路:AbstractRoutingDataSource

Spring提供了一个AbstractRoutingDataSource,它本身不管理连接,而是根据一个determineCurrentLookupKey()方法的返回值,从内部维护的targetDataSourcesMap里选一个真正的DataSource。所以我们要做三件事:

定义多个真实的DataSource(比如master、slave)。

写一个类继承AbstractRoutingDataSource,重写determineCurrentLookupKey(),从ThreadLocal里取当前数据源标识。

写一个AOP切面或注解,在方法执行前把数据源标识塞进ThreadLocal,执行后清理。

实战配置步骤

假设我们有两个库:主库(master)和从库(slave)。首先在application.yml里配两个数据源:

yaml
复制
下载
spring: datasource: master: jdbc-url: jdbc:mysql://localhost:3306/master_db username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver slave: jdbc-url: jdbc:mysql://localhost:3306/slave_db username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver

注意:多数据源时要用jdbc-url而不是url,否则HikariCP会报错。

然后写配置类,创建两个DataSource Bean:

java
复制
下载
@Configuration public class DataSourceConfig { @Bean("master") @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } @Bean("slave") @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaveDataSource() { return DataSourceBuilder.create().build(); } }

接着定义动态数据源类:

java
复制
下载
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSource(); } }

DataSourceContextHolder用ThreadLocal保存当前数据源:

java
复制
下载
public class DataSourceContextHolder { private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>(); public static void setDataSource(String dataSource) { CONTEXT.set(dataSource); } public static String getDataSource() { return CONTEXT.get(); } public static void clear() { CONTEXT.remove(); } }

再注册动态数据源为Primary:

java
复制
下载
@Bean @Primary public DataSource dynamicDataSource(@Qualifier("master") DataSource master, @Qualifier("slave") DataSource slave) { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("master", master); targetDataSources.put("slave", slave); DynamicDataSource dynamicDataSource = new DynamicDataSource(); dynamicDataSource.setTargetDataSources(targetDataSources); dynamicDataSource.setDefaultTargetDataSource(master); return dynamicDataSource; }

最后写一个注解和AOP切面:

java
复制
下载
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface DS { String value() default "master"; }
java
复制
下载
@Aspect @Component public class DsAspect { @Around("@annotation(ds)") public Object around(ProceedingJoinPoint point, DS ds) throws Throwable { DataSourceContextHolder.setDataSource(ds.value()); try { return point.proceed(); } finally { DataSourceContextHolder.clear(); } } }

使用时就很简单:

java
复制
下载
@DS("slave") public List<User> queryUsers() { return userMapper.selectAll(); }

几个必须注意的坑

第一,事务问题。@Transactional默认只作用于一个数据源,如果方法内切换了数据源,事务不会跨库。需要分布式事务时,得用Seata或消息表。

第二,MyBatis集成。如果用了MyBatis,要确保SqlSessionFactory用的是动态数据源,而不是某个具体数据源。通常把动态数据源设为@Primary即可。

第三,数据源切换失效。AOP切面要在Service层生效,如果同类方法自调用,切面不生效。另外,ThreadLocal要在finally里清理,防止线程复用串数据。

第四,连接池监控。多数据源下,每个连接池要单独监控,否则一个池满了你都不知道。

总结

SpringBoot多数据源配置,核心就是AbstractRoutingDataSource+ ThreadLocal + AOP。不要被“动态”“路由”这些词吓到,代码量其实很少。先跑通主从切换,再考虑分库分表、事务一致性。记住:多数据源不是目的,让正确的SQL去正确的库才是。动手试一次,你会发现真的没那么难。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询