在 Spring Boot 中使用 AOP 切面实现多数据源切换是一个非常常见的需求。你可以通过 AOP(面向切面编程)来动态地切换数据源,通常是基于方法的参数或方法名来决定使用哪个数据源。以下是一个基于 Spring Boot 和 AOP 实现多数据源切换的示例。
首先,我们需要在 application.yml
或 application.properties
配置多个数据源。假设我们有两个数据源:datasource1
和 datasource2
。
application.yml
示例:spring:
datasource:
datasource1:
url: jdbc:mysql://localhost:3306/db1
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
datasource2:
url: jdbc:mysql://localhost:3306/db2
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
我们需要创建一个 DynamicDataSource
类,这个类会根据当前的上下文来切换不同的数据源。
DynamicDataSource.java
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
// 获取当前的数据库类型或数据源标识符
return DataSourceContextHolder.getDataSourceType();
}
}
DataSourceContextHolder
负责存储和获取当前使用的数据源类型。我们将数据源类型存储在 ThreadLocal
中,这样每个线程都可以独立的使用一个数据源。
DataSourceContextHolder.java
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
// 设置数据源类型
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
// 获取数据源类型
public static String getDataSourceType() {
return contextHolder.get();
}
// 清除数据源类型
public static void clearDataSourceType() {
contextHolder.remove();
}
}
现在,我们使用 AOP 来拦截方法调用,根据不同的逻辑动态切换数据源。我们可以根据方法上的注解或者方法参数来切换数据源。
DataSourceAspect.java
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class DataSourceAspect {
@Before("@annotation(dataSource)") // 使用注解切换数据源
public void switchDataSource(DataSource dataSource) {
String dataSourceType = dataSource.value();
DataSourceContextHolder.setDataSourceType(dataSourceType);
}
}
为了标识方法使用哪个数据源,我们可以创建一个自定义的注解 @DataSource
,并在需要切换数据源的方法上使用它。
DataSource.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD) // 该注解作用于方法
@Retention(RetentionPolicy.RUNTIME) // 运行时可用
public @interface DataSource {
String value(); // 数据源的标识符
}
配置数据源 Bean,使用 DynamicDataSource
类来管理多个数据源。
DataSourceConfig.java
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
@Configuration
@EnableConfigurationProperties
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 配置默认数据源
dynamicDataSource.setDefaultTargetDataSource(dataSource1());
// 配置多数据源
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put("datasource1", dataSource1());
targetDataSources.put("datasource2", dataSource2());
dynamicDataSource.setTargetDataSources(targetDataSources);
return dynamicDataSource;
}
// 数据源1
@Bean
public DataSource dataSource1() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/db1");
dataSource.setUsername("root");
dataSource.setPassword("password");
return dataSource;
}
// 数据源2
@Bean
public DataSource dataSource2() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/db2");
dataSource.setUsername("root");
dataSource.setPassword("password");
return dataSource;
}
// 配置事务管理器
@Bean
public PlatformTransactionManager transactionManager(@Qualifier("dataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
现在,你可以在方法上使用 @DataSource
注解来指定使用哪个数据源。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@DataSource("datasource1") // 使用数据源1
public void getDataFromDataSource1() {
// 从数据源1获取数据
userRepository.findAll();
}
@DataSource("datasource2") // 使用数据源2
public void getDataFromDataSource2() {
// 从数据源2获取数据
userRepository.findAll();
}
}
通过上述步骤,我们使用了 AOP 切面技术来实现了根据方法注解动态切换数据源。@DataSource
注解标记方法调用时,DataSourceAspect
切面会执行相应的操作,从 ThreadLocal
中获取当前数据源标识并进行切换。这样,你就可以在不同的方法中使用不同的数据库连接,实现多数据源切换。