如何在 Spring Boot 项目中配置多数据源,基于自定义注解 `@TargetDataSource` 实现数据库动态切换,适用于分库分表、读写分离等场景
本文详细介绍如何在 Spring Boot 项目中配置多数据源,基于自定义注解 @TargetDataSource
实现数据库动态切换,适用于分库分表、读写分离等场景。
一、基础配置:多数据源 YAML 配置示例
在 application.yml
中定义多个数据源:
复制
spring:
datasource:
dynamic:
defaultName: default
dataSource:
- name: default
driver: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/database
userName: xxx
password: xxx
- name: two
driver: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
url: jdbc:mysql://localhost:3306/database-2
userName: xxx
password: xxx
二、数据源模型实体类 DataSourceModel
用于创建具体的数据源实例:
复制
@Data
public class DataSourceModel {
private String name;
private String driver;
private String url;
private String userName;
private String password;
private Class<? extends DataSource> type = com.alibaba.druid.pool.DruidDataSource.class;
public DataSource createDataSource() {
return DataSourceBuilder.create()
.driverClassName(this.driver)
.url(this.url)
.username(this.userName)
.password(this.password)
.type(this.type)
.build();
}
}
三、继承 AbstractRoutingDataSource 实现动态数据源切换
核心类:DynamicDataSource
复制
public class DynamicDataSource extends AbstractRoutingDataSource {
private String defaultKey = null;
@Override
protected Object determineCurrentLookupKey() {
if (StringUtil.isEmpty(DataSourceHolder.getDataSourceName())) {
return defaultKey;
}
return DataSourceHolder.getDataSourceName();
}
public DynamicDataSource(String defaultKey, DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
this.defaultKey = defaultKey;
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
}
四、使用 ThreadLocal 储存当前数据源名称
DataSourceHolder
工具类:
复制
public class DataSourceHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
public static void setDataSourceName(String name) {
CONTEXT_HOLDER.set(name);
}
public static String getDataSourceName() {
return CONTEXT_HOLDER.get();
}
public static void clearDataSourceName() {
CONTEXT_HOLDER.remove();
}
}
五、加载配置的所有数据源并设置默认源
DataSourceProperties
配置类:
复制
@Data
@Configuration
@ConfigurationProperties(prefix = "spring.datasource.dynamic")
public class DataSourceProperties {
private List<DataSourceModel> dataSource = new ArrayList<>();
private String defaultDataSourceName = "default";
public DataSourceModel getDefaultDataSourceModel() {
return this.dataSource.stream()
.filter(item -> StringUtil.isEqual(item.getName(), this.defaultDataSourceName))
.findFirst()
.orElse(null);
}
public DataSource getDefaultDataSource() {
DataSourceModel model = getDefaultDataSourceModel();
return model != null ? model.createDataSource() : null;
}
public Map<Object, Object> getTargetDataSource() {
Map<Object, Object> map = new HashMap<>();
dataSource.forEach(ds -> map.put(ds.getName(), ds.createDataSource()));
return map;
}
}
六、自定义数据源注解 @TargetDataSource
支持方法级或类级切换数据源:
复制
@Documented
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface TargetDataSource {
@AliasFor("name") String value() default "default";
@AliasFor("value") String name() default "default";
}
七、注解切面 AOP:自动切换数据源
核心切面 TargetDataSourceAop
实现自动切换逻辑:
复制
@Aspect
@Component
@Slf4j
public class TargetDataSourceAop {
@Pointcut("@annotation(com.xxx.config.datasource.TargetDataSource)")
public void pointCut() {}
@Before("pointCut()")
public void before(JoinPoint joinPoint) {
TargetDataSource targetDataSource = null;
Class<?> targetClass = joinPoint.getTarget().getClass();
if (targetClass != null) {
targetDataSource = targetClass.getAnnotation(TargetDataSource.class);
}
if (targetDataSource == null) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
targetDataSource = signature.getMethod().getAnnotation(TargetDataSource.class);
}
DataSourceHolder.setDataSourceName(targetDataSource.value());
log.info("切换数据源: {}", targetDataSource.value());
}
@After("pointCut()")
public void after(JoinPoint joinPoint) {
DataSourceHolder.clearDataSourceName();
log.info("清空数据源");
}
}
八、总结
通过以上配置与注解切面机制,你可以轻松实现:
- Spring Boot 多数据源支持
- 动态切换数据源逻辑
- 注解式或类级别数据源指定
- 可扩展至读写分离、分库分表、主从配置场景
作者:https://blog.xn--rpv331d.com/望舒
链接:https://blog.xn--rpv331d.com/望舒/blog/19
转载注意保留文章出处...
2
1
89
No data