下面就来详细讲解“SpringBoot基于AbstractRoutingDataSource实现多数据源动态切换”的完整攻略:
什么是AbstractRoutingDataSource
Spring中提供了AbstractRoutingDataSource
抽象类,该抽象类继承自AbstractDataSource
类,用于实现多数据源的动态切换。继承该抽象类并实现其中的determineCurrentLookupKey()
方法,可以根据每次访问时的参数确定该使用哪个数据源,从而实现动态切换多个数据源。
实现多数据源动态切换的步骤
- 首先,需要定义每个数据源的相关配置信息,例如数据库URL、用户名、密码等信息。可以通过在
application.properties
文件中定义多组数据源的方式来实现:
# 主数据源
spring.datasource.url=jdbc:mysql://localhost:3306/test1
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
# 数据源1
datasource1.url=jdbc:mysql://localhost:3306/test2
datasource1.username=root
datasource1.password=123456
datasource1.driver-class-name=com.mysql.jdbc.Driver
# 数据源2
datasource2.url=jdbc:mysql://localhost:3306/test3
datasource2.username=root
datasource2.password=123456
datasource2.driver-class-name=com.mysql.jdbc.Driver
- 接着,在自定义的数据源配置类中添加
@Configuration
和@Primary
注解,以及定义主数据源,并且需要将其他的数据源也注入进来,并对每个数据源分别起一个标识符:
@Configuration
public class DataSourceConfig {
@Primary
@Bean(name = "primaryDataSource")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "dataSource1")
@ConfigurationProperties(prefix = "datasource1")
public DataSource dataSource1() {
return DataSourceBuilder.create().build();
}
@Bean(name = "dataSource2")
@ConfigurationProperties(prefix = "datasource2")
public DataSource dataSource2() {
return DataSourceBuilder.create().build();
}
/**
* 使用DynamicDataSource,避免直接注入dataSource实现动态数据源切换
*
* @return
*/
@Bean(name = "dynamicDataSource")
public DynamicDataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>();
dataSourceMap.put("primaryDataSource", primaryDataSource());
dataSourceMap.put("dataSource1", dataSource1());
dataSourceMap.put("dataSource2", dataSource2());
dynamicDataSource.setDefaultTargetDataSource(primaryDataSource());
dynamicDataSource.setTargetDataSources(dataSourceMap);
return dynamicDataSource;
}
}
- 实现动态切换,创建自定义的数据源类
DynamicDataSource
,该类继承自AbstractRoutingDataSource
,并重写其中的determineCurrentLookupKey()
方法,根据不同的参数,动态地切换使用的数据源:
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceKey();
}
}
- 为了实现更方便的动态切换数据源,需要创建一个线程本地变量
DataSourceContextHolder
,用来保存当前使用的数据源标识符。可以使用ThreadLocal
保证每个线程独立使用自己的数据源:
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSourceKey(String dataSourceKey) {
contextHolder.set(dataSourceKey);
}
public static String getDataSourceKey() {
return contextHolder.get();
}
public static void clearDataSourceKey() {
contextHolder.remove();
}
}
- 最后,在需要访问不同数据源的Service方法上使用
@DataSource
注解,通过注解的参数来指定当前需要使用的数据源标识符,然后将该注解添加到一个切面中:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
/**
* 切换到的数据源名称
*/
String value();
}
@Aspect
@Component
public class DataSourceAspect {
@Pointcut("@annotation(com.example.demo.datasource.annotation.DataSource)")
public void dataSourcePointCut() {
}
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
DataSource dataSource = method.getAnnotation(DataSource.class);
if (dataSource != null) {
String dataSourceKey = dataSource.value();
DataSourceContextHolder.setDataSourceKey(dataSourceKey);
}
try {
return point.proceed();
} finally {
DataSourceContextHolder.clearDataSourceKey();
}
}
}
示例
下面列举两个简单示例:
- 每个数据源的连接配置是由用户输入的:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
@DataSource("primaryDataSource")
public List<User> getAllUsers() {
return userDao.getAllUsers();
}
@Override
public List<User> getAllUsersByDataSource(String url, String userName, String password) {
DynamicDataSource dynamicDataSource = (DynamicDataSource) DataSourceContextHolder.getApplicationContext()
.getBean("dynamicDataSource");
DataSource dataSource = DataSourceBuilder.create()
.driverClassName("com.mysql.cj.jdbc.Driver")
.url(url)
.username(userName)
.password(password)
.build();
dynamicDataSource.addTargetDataSource("externalDataSource", dataSource);
DataSourceContextHolder.setDataSourceKey("externalDataSource");
List<User> userList = userDao.getAllUsers();
DataSourceContextHolder.clearDataSourceKey();
dynamicDataSource.removeTargetDataSource("externalDataSource");
return userList;
}
}
在该示例中,每次访问的数据源需要从方法参数中获取,于是我们可以创建一个新数据源,在使用完毕之后再将其销毁。方法上使用了@DataSource
注解,指定了默认数据源。
- 每个数据源的连接配置固定,并定义了两个不同的数据源:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
@DataSource("primaryDataSource")
public List<User> getAllUsers() {
return userDao.getAllUsers();
}
@Override
@DataSource("dataSource1")
public List<User> getAllUsersByDataSourceA() {
return userDao.getAllUsers();
}
@Override
@DataSource("dataSource2")
public List<User> getAllUsersByDataSourceB() {
return userDao.getAllUsers();
}
}
在该示例中,每个@DataSource
注解都在对应的Service方法上进行了指定。
通过以上步骤和示例,就可以实现多数据源动态切换了。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringBoot基于AbstractRoutingDataSource实现多数据源动态切换 - Python技术站