在项目中,大多数情况下一个数据源便可解决问题,但也存在某些情况下,需要多个数据源的支持,像读写分离,或者业务本身很复杂,涉及到多个数据源。本文基于SpringBoot实现动态数据源配置和切换,可应用于多数据源,读写分离等场景。
数据源配置
分别配置了druid.first和druid.second两个数据源
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.jdbc.Driver
druid:
first:
url: jdbc:mysql://localhost:3306/v-sso?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: 123456
second:
url: jdbc:mysql://localhost:3306/v-sso2?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: 123456
initial-size: 10
max-active: 100
min-idle: 10
##省略一些其他配置信息
动态数据源实现
首先定义一个常量存储所有的数据源定义,这里定义了两个数据源,分别为FIRST和SECOND。
public class DataSourceDef {
public static final String FIRST = "first";
public static final String SECOND = "second";
}
动态数据源切换的核心是结合AbstractRoutingDataSource实现,通过ThreadLocal实现线程内持有数据源,线程之间持有各自的数据源副本。
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
//项目启动默认数据源
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
super.afterPropertiesSet();
}
@Override
protected Object determineCurrentLookupKey() {
return getDataSource();
}
//设置数据源
public static void setDataSource(String dataSource) {
contextHolder.set(dataSource);
}
//获取数据源
public static String getDataSource() {
return contextHolder.get();
}
//释放资源
public static void clearDataSource() {
contextHolder.remove();
}
}
定义一个多数据源配置,分别生成两个数据源对应bean对象,同时通过Map实现常量与数据源的绑定。
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.druid.first")
public DataSource firstDataSource(){
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.druid.second")
public DataSource secondDataSource(){
return DruidDataSourceBuilder.create().build();
}
@Bean
@Primary
public DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource) {
//数据源绑定
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceDef.FIRST, firstDataSource);
targetDataSources.put(DataSourceDef.SECOND, secondDataSource);
return new DynamicDataSource(firstDataSource, targetDataSources);
}
}
至此,我们便实现了多个数据源的配置,但默认的还是第一个数据源,然而我们可能需要切换其他数据源,最好还可以动态切换,这里可通过切面编程实现。
自定义一个数据源注解@DataSource,name则是想要切换的数据源
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String name() default "";
}
通过切面编程实现数据源自动切换
@Aspect
@Component
public class DataSourceAspect {
@Pointcut("@annotation(com.keduw.vortex.anntation.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){
//默认数据源
DynamicDataSource.setDataSource(DataSourceDef.FIRST);
}else {
//使用注解指定的数据源
DynamicDataSource.setDataSource(dataSource.name());
}
try {
return point.proceed();
} finally {
DynamicDataSource.clearDataSource();
}
}
}
到这里基本便实现了多数据源的配置和切换,可以通过@DataSource指定切换的数据源,没有注解的方法则走默认数据源。
测试
核心代码篇幅有点长,这里只放核心代码
public interface TestService {
Test getTestInfo(int id);
//指定数据源的测试方法
Test getTestInfo2(int id);
}
@Service("testService")
public class TestServiceImpl implements TestService {
@Autowired
private TestDao testDao;
@Override
public Test getTestInfo(int id) {
return testDao.selectByPrimaryKey(id);
}
@Override
@DataSource(name = DataSourceDef.SECOND)
public Test getTestInfo2(int id) {
return testDao.selectByPrimaryKey(id);
}
}
运行结果:
小结
以上便是”SpringBoot实现动态数据源配置和切换“的所有内容。如果您有什么疑问或者文章有什么问题,欢迎私信或留言交流~