优化动态数据源

This commit is contained in:
zhouhao
2017-05-19 10:28:17 +08:00
parent 8afa483829
commit 1ca7b9114d
11 changed files with 269 additions and 52 deletions

View File

@@ -1,37 +1,93 @@
package org.hswebframework.web.datasource;
import org.hswebframework.web.datasource.exception.DataSourceNotFoundException;
import org.hswebframework.web.datasource.switcher.DataSourceSwitcher;
/**
* TODO 完成注释
* 用于操作动态数据源,如获取当前使用的数据源,使用switcher切换数据源等
*
* @author zhouhao
* @see 3.0
*/
public final class DataSourceHolder {
/**
* 动态数据源切换器
*/
static DataSourceSwitcher dataSourceSwitcher;
/**
* 动态数据源服务
*/
static DynamicDataSourceService dynamicDataSourceService;
/**
* @return 动态数据源切换器
*/
public static DataSourceSwitcher switcher() {
return dataSourceSwitcher;
}
public static DynamicDataSource getDefaultDataSource() {
/**
* @return 默认数据源
*/
public static DynamicDataSource defaultDataSource() {
return dynamicDataSourceService.getDefaultDataSource();
}
public static DynamicDataSource getActiveDataSource() {
/**
* @return 当前使用的数据源
*/
public static DynamicDataSource currentDataSource() {
String id = dataSourceSwitcher.currentDataSourceId();
if (id == null) return getDefaultDataSource();
if (id == null) return defaultDataSource();
return dynamicDataSourceService.getDataSource(id);
}
public static DatabaseType getActiveDatabaseType() {
return getActiveDataSource().getType();
/**
* @return 当前使用的数据源是否为默认数据源
*/
public static boolean currentIsDefault() {
return dataSourceSwitcher.currentDataSourceId() == null;
}
public static DatabaseType getDefaultDatabaseType() {
return getDefaultDataSource().getType();
/**
* 判断指定id的数据源是否存在
*
* @param id 数据源id {@link DynamicDataSource#getId()}
* @return 数据源是否存在
*/
public static boolean existing(String id) {
try {
return dynamicDataSourceService.getDataSource(id) != null;
} catch (DataSourceNotFoundException e) {
return false;
}
}
/**
* @return 当前使用的数据源是否存在
*/
public static boolean currentExisting() {
if (currentIsDefault()) return true;
try {
return currentDataSource() != null;
} catch (DataSourceNotFoundException e) {
return false;
}
}
/**
* @return 当前数据库类型
*/
public static DatabaseType currentDatabaseType() {
return currentDataSource().getType();
}
/**
* @return 默认的数据库类型
*/
public static DatabaseType defaultDatabaseType() {
return defaultDataSource().getType();
}
}

View File

@@ -1,5 +1,8 @@
package org.hswebframework.web.datasource.annotation;
import org.hswebframework.web.datasource.DataSourceHolder;
import org.hswebframework.web.datasource.DynamicDataSource;
import java.lang.annotation.*;
/**
@@ -10,5 +13,17 @@ import java.lang.annotation.*;
@Documented
@Inherited
public @interface UseDataSource {
/**
* @return 数据源ID ,支持表达式如 : ${#param.id}
* @see DynamicDataSource#getId()
*/
String value();
/**
* @return 数据源不存在时, 是否使用默认数据源.
* 如果为{@code false},当数据源不存在的时候,
* 将抛出 {@link org.hswebframework.web.datasource.exception.DataSourceNotFoundException}
* @see DataSourceHolder#currentExisting()
*/
boolean fallbackDefault() default true;
}

View File

@@ -4,6 +4,7 @@ import org.hswebframework.web.datasource.DynamicDataSource;
import org.hswebframework.web.datasource.DynamicDataSourceProxy;
import org.hswebframework.web.datasource.DynamicDataSourceService;
import javax.annotation.PreDestroy;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Map;
@@ -25,6 +26,11 @@ public abstract class AbstractDynamicDataSourceService implements DynamicDataSou
this(dataSource instanceof DynamicDataSource ? (DynamicDataSource) dataSource : new DynamicDataSourceProxy(null, dataSource));
}
@PreDestroy
public void destroy() {
dataSourceStore.values().forEach(DataSourceCache::closeDataSource);
}
@Override
public DynamicDataSource getDataSource(String dataSourceId) {
DataSourceCache cache = dataSourceStore.get(dataSourceId);

View File

@@ -0,0 +1,78 @@
package org.hswebframework.web.datasource.starter;
import org.aopalliance.intercept.MethodInterceptor;
import org.hswebframework.web.AopUtils;
import org.hswebframework.web.ExpressionUtils;
import org.hswebframework.web.boost.aop.context.MethodInterceptorHolder;
import org.hswebframework.web.datasource.DataSourceHolder;
import org.hswebframework.web.datasource.annotation.UseDataSource;
import org.hswebframework.web.datasource.annotation.UseDefaultDataSource;
import org.hswebframework.web.datasource.exception.DataSourceNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Method;
/**
* 通过aop方式进行对注解方式切换数据源提供支持
*
* @author zhouhao
* @since 3.0
*/
@Configuration
public class AopDataSourceSwitcherAutoConfiguration {
@Bean
public SwitcherMethodMatcherPointcutAdvisor switcherMethodMatcherPointcutAdvisor() {
return new SwitcherMethodMatcherPointcutAdvisor();
}
public static class SwitcherMethodMatcherPointcutAdvisor extends StaticMethodMatcherPointcutAdvisor {
private static final Logger logger = LoggerFactory.getLogger(SwitcherMethodMatcherPointcutAdvisor.class);
public SwitcherMethodMatcherPointcutAdvisor() {
setAdvice((MethodInterceptor) methodInvocation -> {
logger.debug("switch datasource...");
UseDataSource useDataSource = AopUtils.findAnnotation(methodInvocation.getThis().getClass(),
methodInvocation.getMethod(), UseDataSource.class);
if (useDataSource != null) {
String id = useDataSource.value();
if (id.contains("${")) {
MethodInterceptorHolder holder = MethodInterceptorHolder.create(methodInvocation);
id = ExpressionUtils.analytical(id, holder.getArgs(), "spel");
}
if (!DataSourceHolder.existing(id)) {
if (useDataSource.fallbackDefault()) {
DataSourceHolder.switcher().useDefault();
} else {
throw new DataSourceNotFoundException(id);
}
} else {
DataSourceHolder.switcher().use(id);
}
} else {
UseDefaultDataSource useDefaultDataSource = AopUtils.findAnnotation(methodInvocation.getThis().getClass(),
methodInvocation.getMethod(), UseDefaultDataSource.class);
if (useDefaultDataSource == null) {
logger.warn("can't found annotation: UseDefaultDataSource !");
}
DataSourceHolder.switcher().useDefault();
}
try {
return methodInvocation.proceed();
} finally {
DataSourceHolder.switcher().useLast();
}
});
}
@Override
public boolean matches(Method method, Class<?> aClass) {
return AopUtils.findAnnotation(aClass, method, UseDataSource.class) != null ||
AopUtils.findAnnotation(aClass, method, UseDefaultDataSource.class) != null;
}
}
}

View File

@@ -1,9 +1,11 @@
package org.hswebframework.web.datasource.switcher;
/**
* TODO 完成注释
* 动态数据源切换器,用于切换数据源操作
*
* @author zhouhao
* @see 3.0
* @see DefaultDataSourceSwitcher
*/
public interface DataSourceSwitcher {
@@ -25,12 +27,12 @@ public interface DataSourceSwitcher {
void useDefault();
/**
* @return 当前选择的数据源ID
* @return 当前选择的数据源ID, 如果为默认数据源则返回 {@code null}
*/
String currentDataSourceId();
/**
* 重置切换记录
* 重置切换记录,重置后,使用默认数据源
*/
void reset();
}

View File

@@ -8,26 +8,31 @@ import java.util.Deque;
import java.util.LinkedList;
/**
* TODO 完成注释
* 默认的动态数据源切换器,基于ThreadLocal,queue
*
* @author zhouhao
* @since 3.0
*/
public class DefaultDataSourceSwitcher implements DataSourceSwitcher {
//默认数据源标识
private static final String DEFAULT_DATASOURCE_ID = DataSourceSwitcher.class.getName() + "_default_";
private Logger logger = LoggerFactory.getLogger(this.getClass());
private Deque<String> getUsedHistoryQueue() {
// 从ThreadLocal中获取一个使用记录
return ThreadLocalUtils.get(DefaultDataSourceSwitcher.class.getName() + "_queue", LinkedList::new);
}
@Override
public void useLast() {
// 没有上一次了
if (getUsedHistoryQueue().size() == 0) {
return;
}
//移除队尾,则当前的队尾则为上一次的数据源
getUsedHistoryQueue().removeLast();
if (logger.isDebugEnabled()) {
String current = currentDataSourceId();
if (null != current)
@@ -38,6 +43,7 @@ public class DefaultDataSourceSwitcher implements DataSourceSwitcher {
@Override
public void use(String dataSourceId) {
//添加对队尾
getUsedHistoryQueue().addLast(dataSourceId);
if (logger.isDebugEnabled()) {
logger.debug("try use data source : {}", dataSourceId);