如何在Spring Boot运行时根据SOAP接口参数切换datasource数据源
Spring Boot 动态切换双数据源实现方案
你可以通过Spring提供的AbstractRoutingDataSource结合线程上下文变量实现按需切换数据源,完整步骤如下:
1. 定义数据源上下文持有类
用ThreadLocal存储当前线程使用的数据源标识,保证线程隔离:
public class DataSourceContextHolder { private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>(); // 设置数据源key public static void setDataSourceKey(String key) { CONTEXT.set(key); } // 获取数据源key public static String getDataSourceKey() { return CONTEXT.get(); } // 清空数据源key,避免线程复用导致问题 public static void clear() { CONTEXT.remove(); } }
2. 实现动态数据源路由类
继承AbstractRoutingDataSource,重写查找数据源key的方法:
public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSourceKey(); } }
3. 注册多数据源配置
首先在启动类排除Spring默认的数据源自动配置,避免冲突:
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) public class YourApplication { public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } }
然后编写数据源配置类,把两个数据源和动态路由数据源注册为Bean:
@Configuration public class DataSourceConfig { // 配置reale数据源 @Bean @ConfigurationProperties(prefix = "spring.datasource.reale") public DataSource realeDataSource() { return DataSourceBuilder.create().build(); } // 配置virtuale数据源 @Bean @ConfigurationProperties(prefix = "spring.datasource.virtuale") public DataSource virtualeDataSource() { return DataSourceBuilder.create().build(); } // 注册动态数据源为默认主数据源 @Bean @Primary public DataSource dynamicDataSource() { DynamicDataSource dynamicDataSource = new DynamicDataSource(); // 设置所有可选数据源 Map<Object, Object> dataSourceMap = new HashMap<>(); dataSourceMap.put("reale", realeDataSource()); dataSourceMap.put("virtuale", virtualeDataSource()); dynamicDataSource.setTargetDataSources(dataSourceMap); // 设置默认数据源为reale dynamicDataSource.setDefaultTargetDataSource(realeDataSource()); return dynamicDataSource; } }
注意@ConfigurationProperties的前缀要和你bootstrap.yml里两个数据源的配置前缀保持一致。
4. 在SOAP接口中按需切换数据源
在你的@Endpoint注解的SOAP接口处理方法中,拿到入参后判断,设置对应的数据源key,方法执行完后清空上下文:
@Endpoint public class YourSoapEndpoint { // 你的SOAP接口处理方法 @PayloadRoot(namespace = "你的命名空间", localPart = "你的请求节点名") public @ResponsePayload YourResponse handleRequest(@RequestPayload YourRequest request) { try { // 根据入参判断要使用的数据源 if (/* 校验参数符合virtuale使用条件 */) { DataSourceContextHolder.setDataSourceKey("virtuale"); } else { DataSourceContextHolder.setDataSourceKey("reale"); } // 执行业务逻辑,操作数据库会自动走对应数据源 // ... 你的业务代码 } finally { // 必须清空上下文,避免线程池复用线程导致数据源错乱 DataSourceContextHolder.clear(); } return response; } }
注意事项
- 若你的业务逻辑使用了事务,需要保证切换数据源的操作在事务开启之前执行,否则切换不会生效
- 必须在finally块中清空ThreadLocal存储的数据源标识,避免内存泄漏和线程复用导致的数据源串用问题
- 如果你用了MyBatis等ORM框架,不需要额外修改Mapper层代码,只要动态数据源注册为主数据源即可正常适配
内容的提问来源于stack exchange,提问作者Mauraglia
相关产品推荐
相关产品推荐

