在项目中需要用到多数据源的切换,主要用到AbstractRoutingDataSource类来实现这个功能。
AbstractRoutingDataSource是Spring-jdbc中一个调用路由到各种目标 DataSource 之一的抽象类。
通过determineCurrentLookupKey()方法来指定路由,切换数据源通过AOP加自定义注解实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
@Nullable private Map<Object, Object> targetDataSources;
@Nullable private Object defaultTargetDataSource;
@Nullable private Map<Object, DataSource> resolvedDataSources; @Override public void afterPropertiesSet() { if (this.targetDataSources == null) { throw new IllegalArgumentException("Property 'targetDataSources' is required"); } this.resolvedDataSources = CollectionUtils.newHashMap(this.targetDataSources.size()); this.targetDataSources.forEach((key, value) -> { Object lookupKey = resolveSpecifiedLookupKey(key); DataSource dataSource = resolveSpecifiedDataSource(value); this.resolvedDataSources.put(lookupKey, dataSource); }); if (this.defaultTargetDataSource != null) { this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource); } }
|
代码如下:
DynamicDataSource
1 2 3 4 5 6 7 8 9
| public class DynamicDataSource extends AbstractRoutingDataSource {
@Override protected Object determineCurrentLookupKey() { return DbContextHolder.getDbType(); } }
|
DbContextHolder
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class DbContextHolder { private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
public static void setDbType(String dbType) { CONTEXT_HOLDER.set(dbType); }
public static String getDbType() { String type = CONTEXT_HOLDER.get(); if (StringUtils.isEmpty(type)) { return null; } return type; }
public static void clearDbType() { CONTEXT_HOLDER.remove(); }
|
DatasourceConfig
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| @Configuration public class DatasourceConfig implements EnvironmentAware {
private static String BASE_PATH = "spring.datasource.druid.";
private Environment environment;
@Override public void setEnvironment(Environment environment) { this.environment = environment; }
@Bean("dynamicDatasource") public DynamicDataSource dynamicDataSource() { String dbNames = environment.getProperty("spring.datasource.names"); String[] split = dbNames.split(","); Map<Object, Object> targetDataSources = new LinkedHashMap<>(2 << 2); DynamicDataSource dynamicDataSource = new DynamicDataSource(); for (String str : split) { DruidDataSource druidDataSource = initDruidDataSource(str); targetDataSources.put(str, druidDataSource); } dynamicDataSource.setTargetDataSources(targetDataSources); dynamicDataSource.setDefaultTargetDataSource(targetDataSources.entrySet().iterator().next().getValue()); return dynamicDataSource; }
public DruidDataSource initDruidDataSource(String dbName) { String driverClassName = environment.getProperty(BASE_PATH + dbName + ".driver-class-name"); String url = environment.getProperty(BASE_PATH + dbName + ".url"); String username = environment.getProperty(BASE_PATH + dbName + ".username"); String password = environment.getProperty(BASE_PATH + dbName + ".password"); DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.setDriverClassName(driverClassName); druidDataSource.setUrl(url); druidDataSource.setUsername(username); druidDataSource.setPassword(password); return druidDataSource; } }
|
DS
1 2 3 4 5 6 7 8
| @Retention(RetentionPolicy.RUNTIME) @Documented @Target(ElementType.METHOD) public @interface DS { String value(); }
|
DSAspect
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Aspect @Component public class DSAspect {
@Around(value = "@annotation(ds)") public Object around(final ProceedingJoinPoint joinPoint, DS ds) throws Throwable { try { String value = ds.value(); DbContextHolder.setDbType(value); Object proceed = joinPoint.proceed(); return proceed; }catch (Throwable throwable) { throw throwable; }finally { DbContextHolder.clearDbType(); } } }
|