|
@@ -2,11 +2,10 @@ package org.hswebframework.web.datasource;
|
|
|
|
|
|
import org.aopalliance.intercept.MethodInterceptor;
|
|
|
import org.hswebframework.web.ExpressionUtils;
|
|
|
+import org.hswebframework.web.boost.aop.context.MethodInterceptorContext;
|
|
|
import org.hswebframework.web.boost.aop.context.MethodInterceptorHolder;
|
|
|
import org.hswebframework.web.datasource.exception.DataSourceNotFoundException;
|
|
|
-import org.hswebframework.web.datasource.strategy.AnnotationDataSourceSwitchStrategyMatcher;
|
|
|
-import org.hswebframework.web.datasource.strategy.DataSourceSwitchStrategyMatcher;
|
|
|
-import org.hswebframework.web.datasource.strategy.ExpressionDataSourceSwitchStrategyMatcher;
|
|
|
+import org.hswebframework.web.datasource.strategy.*;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
|
|
@@ -19,6 +18,8 @@ import java.lang.reflect.Method;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
+import java.util.function.Consumer;
|
|
|
+import java.util.function.Function;
|
|
|
|
|
|
import static org.hswebframework.web.datasource.strategy.AnnotationDataSourceSwitchStrategyMatcher.*;
|
|
|
|
|
@@ -43,8 +44,25 @@ public class AopDataSourceSwitcherAutoConfiguration {
|
|
|
}
|
|
|
|
|
|
@Bean
|
|
|
- public SwitcherMethodMatcherPointcutAdvisor switcherMethodMatcherPointcutAdvisor(List<DataSourceSwitchStrategyMatcher> matchers) {
|
|
|
- return new SwitcherMethodMatcherPointcutAdvisor(matchers);
|
|
|
+ public TableSwitchStrategyMatcher alwaysNoMatchStrategyMatcher() {
|
|
|
+ return new TableSwitchStrategyMatcher() {
|
|
|
+ @Override
|
|
|
+ public boolean match(Class target, Method method) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public Strategy getStrategy(MethodInterceptorContext context) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ @Bean
|
|
|
+ public SwitcherMethodMatcherPointcutAdvisor switcherMethodMatcherPointcutAdvisor(
|
|
|
+ List<DataSourceSwitchStrategyMatcher> matchers,
|
|
|
+ List<TableSwitchStrategyMatcher> tableSwitcher) {
|
|
|
+ return new SwitcherMethodMatcherPointcutAdvisor(matchers, tableSwitcher);
|
|
|
}
|
|
|
|
|
|
public static class SwitcherMethodMatcherPointcutAdvisor extends StaticMethodMatcherPointcutAdvisor {
|
|
@@ -53,45 +71,79 @@ public class AopDataSourceSwitcherAutoConfiguration {
|
|
|
|
|
|
private List<DataSourceSwitchStrategyMatcher> matchers;
|
|
|
|
|
|
- private Map<AnnotationDataSourceSwitchStrategyMatcher.CacheKey, DataSourceSwitchStrategyMatcher> cache = new ConcurrentHashMap<>();
|
|
|
+ private List<TableSwitchStrategyMatcher> tableSwitcher;
|
|
|
+
|
|
|
+ private Map<CachedDataSourceSwitchStrategyMatcher.CacheKey, DataSourceSwitchStrategyMatcher> cache
|
|
|
+ = new ConcurrentHashMap<>();
|
|
|
+ private Map<CachedTableSwitchStrategyMatcher.CacheKey, TableSwitchStrategyMatcher> tableCache
|
|
|
+ = new ConcurrentHashMap<>();
|
|
|
|
|
|
- public SwitcherMethodMatcherPointcutAdvisor(List<DataSourceSwitchStrategyMatcher> matchers) {
|
|
|
+ public SwitcherMethodMatcherPointcutAdvisor(List<DataSourceSwitchStrategyMatcher> matchers,
|
|
|
+ List<TableSwitchStrategyMatcher> tableSwitcher) {
|
|
|
this.matchers = matchers;
|
|
|
+ this.tableSwitcher = tableSwitcher;
|
|
|
setAdvice((MethodInterceptor) methodInvocation -> {
|
|
|
CacheKey key = new CacheKey(ClassUtils.getUserClass(methodInvocation.getThis()), methodInvocation.getMethod());
|
|
|
+ CachedTableSwitchStrategyMatcher.CacheKey tableKey = new CachedTableSwitchStrategyMatcher.CacheKey(ClassUtils.getUserClass(methodInvocation.getThis()), methodInvocation.getMethod());
|
|
|
+
|
|
|
DataSourceSwitchStrategyMatcher matcher = cache.get(key);
|
|
|
- if (matcher == null) {
|
|
|
- logger.warn("method:{} not support switch datasource", methodInvocation.getMethod());
|
|
|
- } else {
|
|
|
- MethodInterceptorHolder holder = MethodInterceptorHolder.create(methodInvocation);
|
|
|
- Strategy strategy = matcher.getStrategy(holder.createParamContext());
|
|
|
- if (strategy == null) {
|
|
|
- logger.warn("strategy matcher found:{}, but strategy is null!", matcher);
|
|
|
- } else {
|
|
|
- logger.debug("switch datasource.use strategy:{}", strategy);
|
|
|
- if (strategy.isUseDefaultDataSource()) {
|
|
|
- DataSourceHolder.switcher().useDefault();
|
|
|
+ TableSwitchStrategyMatcher tableMatcher = tableCache.get(tableKey);
|
|
|
+
|
|
|
+ Consumer<MethodInterceptorContext> before = context -> {
|
|
|
+ };
|
|
|
+
|
|
|
+ if (matcher != null) {
|
|
|
+ before = before.andThen(context -> {
|
|
|
+ Strategy strategy = matcher.getStrategy(context);
|
|
|
+ if (strategy == null) {
|
|
|
+ logger.warn("strategy matcher found:{}, but strategy is null!", matcher);
|
|
|
} else {
|
|
|
- String id = strategy.getDataSourceId();
|
|
|
- if (id.contains("${")) {
|
|
|
- id = ExpressionUtils.analytical(id, holder.getArgs(), "spel");
|
|
|
- }
|
|
|
- if (!DataSourceHolder.existing(id)) {
|
|
|
- if (strategy.isFallbackDefault()) {
|
|
|
- DataSourceHolder.switcher().useDefault();
|
|
|
- } else {
|
|
|
- throw new DataSourceNotFoundException(id);
|
|
|
- }
|
|
|
+ logger.debug("switch datasource.use strategy:{}", strategy);
|
|
|
+ if (strategy.isUseDefaultDataSource()) {
|
|
|
+ DataSourceHolder.switcher().useDefault();
|
|
|
} else {
|
|
|
- DataSourceHolder.switcher().use(id);
|
|
|
+ try {
|
|
|
+ String id = strategy.getDataSourceId();
|
|
|
+ if (id.contains("${")) {
|
|
|
+ id = ExpressionUtils.analytical(id, context.getParams(), "spel");
|
|
|
+ }
|
|
|
+ if (!DataSourceHolder.existing(id)) {
|
|
|
+ if (strategy.isFallbackDefault()) {
|
|
|
+ DataSourceHolder.switcher().useDefault();
|
|
|
+ } else {
|
|
|
+ throw new DataSourceNotFoundException(id);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ DataSourceHolder.switcher().use(id);
|
|
|
+ }
|
|
|
+ } catch (RuntimeException e) {
|
|
|
+ throw e;
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException(e.getMessage(), e);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (tableMatcher != null) {
|
|
|
+ before = before.andThen(context -> {
|
|
|
+ TableSwitchStrategyMatcher.Strategy strategy = tableMatcher.getStrategy(context);
|
|
|
+ if (null != strategy) {
|
|
|
+ logger.debug("switch table. use strategy:{}", strategy);
|
|
|
+ strategy.getMapping().forEach(DataSourceHolder.tableSwitcher()::use);
|
|
|
+ } else {
|
|
|
+ logger.warn("table strategy matcher found:{}, but strategy is null!", matcher);
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
+
|
|
|
+ MethodInterceptorHolder holder = MethodInterceptorHolder.create(methodInvocation);
|
|
|
+ before.accept(holder.createParamContext());
|
|
|
try {
|
|
|
return methodInvocation.proceed();
|
|
|
} finally {
|
|
|
DataSourceHolder.switcher().useLast();
|
|
|
+ DataSourceHolder.tableSwitcher().reset();
|
|
|
}
|
|
|
});
|
|
|
}
|
|
@@ -103,7 +155,20 @@ public class AopDataSourceSwitcherAutoConfiguration {
|
|
|
.filter(matcher -> matcher.match(aClass, method))
|
|
|
.findFirst()
|
|
|
.ifPresent((matcher) -> cache.put(key, matcher));
|
|
|
- return cache.containsKey(key);
|
|
|
+
|
|
|
+
|
|
|
+ boolean datasourceMatched = cache.containsKey(key);
|
|
|
+ boolean tableMatched = false;
|
|
|
+ if (null != tableSwitcher) {
|
|
|
+ CachedTableSwitchStrategyMatcher.CacheKey tableCacheKey = new CachedTableSwitchStrategyMatcher.CacheKey(aClass, method);
|
|
|
+ tableSwitcher.stream()
|
|
|
+ .filter(matcher -> matcher.match(aClass, method))
|
|
|
+ .findFirst()
|
|
|
+ .ifPresent((matcher) -> tableCache.put(tableCacheKey, matcher));
|
|
|
+ tableMatched = tableCache.containsKey(tableCacheKey);
|
|
|
+ }
|
|
|
+
|
|
|
+ return datasourceMatched || tableMatched;
|
|
|
}
|
|
|
}
|
|
|
}
|