一、动态数据源切换的核心原理
在静态多数据源配置的基础上,动态切换数据源需要借助Spring提供的AbstractRoutingDataSource抽象类。这个类的核心作用是在运行时根据用户定义的规则动态选择数据源,其工作原理是通过重写determineCurrentLookupKey()方法,返回当前线程需要使用的数据源标识,从而实现数据源的动态路由。
为了保证线程安全,我们通常使用ThreadLocal来存储当前线程的数据源标识。ThreadLocal可以为每个线程提供独立的变量副本,不同线程之间的数据源标识互不干扰,避免了多线程环境下的数据源混乱问题。
二、动态数据源切换的代码实现
(一)定义数据源上下文持有者
首先创建一个DataSourceContextHolder类,用于管理当前线程的数据源标识:
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSource(String dataSourceName) {
contextHolder.set(dataSourceName);
}
public static String getDataSource() {
return contextHolder.get();
}
public static void clearDataSource() {
contextHolder.remove();
}
}
这个类通过ThreadLocal来存储和获取数据源标识,确保每个线程都能独立管理自己的数据源。
(二)实现动态数据源路由类
接下来创建DynamicDataSource类,继承自AbstractRoutingDataSource,并重写determineCurrentLookupKey()方法:
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}
这个方法会返回当前线程的数据源标识,AbstractRoutingDataSource会根据这个标识来选择对应的数据源。
(三)配置动态数据源
在配置类中,我们需要将多个数据源注册到Spring容器中,并创建动态数据源实例:
@Configuration
public class DynamicDataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.postgres")
public DataSource postgresDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.sqlserver")
public DataSource sqlserverDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("postgres", postgresDataSource());
dataSourceMap.put("sqlserver", sqlserverDataSource());
dynamicDataSource.setTargetDataSources(dataSourceMap);
dynamicDataSource.setDefaultTargetDataSource(postgresDataSource());
return dynamicDataSource;
}
}
这里我们将PostgreSQL和SQL Server数据源注册到动态数据源中,并设置PostgreSQL为默认数据源。
(四)使用AOP实现数据源切换
为了更方便地切换数据源,我们可以使用AOP技术,通过自定义注解来标记需要切换数据源的方法:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
String value() default "postgres";
}
然后创建一个切面类,在方法执行前根据注解的值切换数据源:
@Aspect
@Component
public class DataSourceAspect {
@Before("@annotation(dataSource)")
public void beforeSwitchDataSource(JoinPoint point, DataSource dataSource) {
String dataSourceName = dataSource.value();
DataSourceContextHolder.setDataSource(dataSourceName);
}
@After("@annotation(dataSource)")
public void afterSwitchDataSource(JoinPoint point, DataSource dataSource) {
DataSourceContextHolder.clearDataSource();
}
}
这样,在需要切换数据源的方法上添加@DataSource注解,就可以实现数据源的动态切换。
三、动态数据源切换的测试与验证
最后,我们可以编写一个测试类来验证动态数据源切换的功能:
@SpringBootTest
public class DynamicDataSourceTest {
@Autowired
private UserMapper userMapper;
@Autowired
private OrderMapper orderMapper;
@Test
public void testDynamicDataSource() {
// 使用默认数据源(PostgreSQL)查询用户
List<User> users = userMapper.selectList(null);
System.out.println("PostgreSQL中的用户数据:" + users);
// 切换到SQL Server数据源查询订单
DataSourceContextHolder.setDataSource("sqlserver");
List<Order> orders = orderMapper.selectList(null);
System.out.println("SQL Server中的订单数据:" + orders);
// 清除数据源标识
DataSourceContextHolder.clearDataSource();
}
}
通过这个测试,我们可以验证动态数据源切换的功能是否正常工作。
四、总结
通过使用AbstractRoutingDataSource和AOP技术,我们可以在Spring Boot项目中实现PostgreSQL和SQL Server的动态数据源切换。这种方式不仅提高了代码的灵活性和可维护性,还能满足复杂业务场景下的多数据源需求。在实际项目中,我们可以根据具体业务需求,进一步优化数据源切换的逻辑,例如实现读写分离、分库分表等功能。