mirror of
https://github.com/hs-web/hsweb-framework.git
synced 2026-06-01 18:35:37 +08:00
优化动态数据源
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user