Ver código fonte

优化数据源切换策略

zhouhao 6 anos atrás
pai
commit
7e3f7c6b8c
12 arquivos alterados com 412 adições e 101 exclusões
  1. 14 0
      hsweb-datasource/README.md
  2. 62 31
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/AopDataSourceSwitcherAutoConfiguration.java
  3. 1 1
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/annotation/UseDataSource.java
  4. 47 0
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/strategy/AnnotationDataSourceSwitchStrategyMatcher.java
  5. 66 0
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/strategy/CachedDataSourceSwitchStrategyMatcher.java
  6. 60 0
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/strategy/DataSourceSwitchStrategyMatcher.java
  7. 67 0
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/strategy/ExpressionDataSourceSwitchStrategyMatcher.java
  8. 0 1
      hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/AtomikosDataSourceConfig.java
  9. 12 14
      hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/InMemoryAtomikosDataSourceRepository.java
  10. 45 49
      hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/JtaDynamicDataSourceService.java
  11. 30 3
      hsweb-datasource/hsweb-datasource-jta/src/test/java/org/hswebframework/web/datasource/jta/SimpleAtomikosTests.java
  12. 8 2
      hsweb-datasource/hsweb-datasource-jta/src/test/resources/application.yml

+ 14 - 0
hsweb-datasource/README.md

@@ -5,6 +5,20 @@
 
 # example
 
+表达式方式:
+
+application.yml配置
+```xml
+hsweb:
+    datasource:
+        switcher:
+           test: # 只是一个标识
+              # 拦截类和方法的表达式
+              expression: org.hswebframework.**.*Service.find*
+              # 使用数据源
+              data-source-id: read_db
+```
+
 编程方式:
 ```java
   //切换到 id为mysql_read_01的数据源

+ 62 - 31
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/AopDataSourceSwitcherAutoConfiguration.java

@@ -1,20 +1,26 @@
 package org.hswebframework.web.datasource;
 
 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.hswebframework.web.datasource.strategy.AnnotationDataSourceSwitchStrategyMatcher;
+import org.hswebframework.web.datasource.strategy.DataSourceSwitchStrategyMatcher;
+import org.hswebframework.web.datasource.strategy.ExpressionDataSourceSwitchStrategyMatcher;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;
+import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.util.ClassUtils;
 
 import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static org.hswebframework.web.datasource.strategy.AnnotationDataSourceSwitchStrategyMatcher.*;
 
 /**
  * 通过aop方式进行对注解方式切换数据源提供支持
@@ -26,40 +32,61 @@ import java.lang.reflect.Method;
 public class AopDataSourceSwitcherAutoConfiguration {
 
     @Bean
-    public SwitcherMethodMatcherPointcutAdvisor switcherMethodMatcherPointcutAdvisor() {
-        return new SwitcherMethodMatcherPointcutAdvisor();
+    @ConfigurationProperties(prefix = "hsweb.datasource")
+    public ExpressionDataSourceSwitchStrategyMatcher expressionDataSourceSwitchStrategyMatcher() {
+        return new ExpressionDataSourceSwitchStrategyMatcher();
+    }
+
+    @Bean
+    public AnnotationDataSourceSwitchStrategyMatcher annotationDataSourceSwitchStrategyMatcher() {
+        return new AnnotationDataSourceSwitchStrategyMatcher();
+    }
+
+    @Bean
+    public SwitcherMethodMatcherPointcutAdvisor switcherMethodMatcherPointcutAdvisor(List<DataSourceSwitchStrategyMatcher> matchers) {
+        return new SwitcherMethodMatcherPointcutAdvisor(matchers);
     }
 
     public static class SwitcherMethodMatcherPointcutAdvisor extends StaticMethodMatcherPointcutAdvisor {
-        private static final Logger logger = LoggerFactory.getLogger(SwitcherMethodMatcherPointcutAdvisor.class);
+        private static final Logger logger           = LoggerFactory.getLogger(SwitcherMethodMatcherPointcutAdvisor.class);
+        private static final long   serialVersionUID = 536295121851990398L;
+
+        private List<DataSourceSwitchStrategyMatcher> matchers;
 
-        public SwitcherMethodMatcherPointcutAdvisor() {
+        private Map<AnnotationDataSourceSwitchStrategyMatcher.CacheKey, DataSourceSwitchStrategyMatcher> cache = new ConcurrentHashMap<>();
+
+        public SwitcherMethodMatcherPointcutAdvisor(List<DataSourceSwitchStrategyMatcher> matchers) {
+            this.matchers = matchers;
             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()) {
+                CacheKey key = new 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();
                         } else {
-                            throw new DataSourceNotFoundException(id);
+                            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);
+                                }
+                            } else {
+                                DataSourceHolder.switcher().use(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();
@@ -71,8 +98,12 @@ public class AopDataSourceSwitcherAutoConfiguration {
 
         @Override
         public boolean matches(Method method, Class<?> aClass) {
-            return AopUtils.findAnnotation(aClass, method, UseDataSource.class) != null ||
-                    AopUtils.findAnnotation(aClass, method, UseDefaultDataSource.class) != null;
+            CacheKey key = new CacheKey(aClass, method);
+            matchers.stream()
+                    .filter(matcher -> matcher.match(aClass, method))
+                    .findFirst()
+                    .ifPresent((matcher) -> cache.put(key, matcher));
+            return cache.containsKey(key);
         }
     }
 }

+ 1 - 1
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/annotation/UseDataSource.java

@@ -25,5 +25,5 @@ public @interface UseDataSource {
      * 将抛出 {@link org.hswebframework.web.datasource.exception.DataSourceNotFoundException}
      * @see DataSourceHolder#currentExisting()
      */
-    boolean fallbackDefault() default true;
+    boolean fallbackDefault() default false;
 }

+ 47 - 0
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/strategy/AnnotationDataSourceSwitchStrategyMatcher.java

@@ -0,0 +1,47 @@
+package org.hswebframework.web.datasource.strategy;
+
+import org.hswebframework.web.AopUtils;
+import org.hswebframework.web.datasource.annotation.UseDataSource;
+import org.hswebframework.web.datasource.annotation.UseDefaultDataSource;
+
+import java.lang.reflect.Method;
+
+/**
+ * @author zhouhao
+ * @since 3.0.0-RC
+ */
+public class AnnotationDataSourceSwitchStrategyMatcher extends CachedDataSourceSwitchStrategyMatcher {
+    @Override
+    public Strategy createStrategyIfMatch(Class target, Method method) {
+        UseDataSource useDataSource = AopUtils.findAnnotation(target, method, UseDataSource.class);
+        UseDefaultDataSource useDefaultDataSource = AopUtils.findAnnotation(target, method, UseDefaultDataSource.class);
+
+        boolean support = useDataSource != null || useDefaultDataSource != null;
+        if (support) {
+            return new Strategy() {
+                @Override
+                public boolean isUseDefaultDataSource() {
+                    return useDefaultDataSource != null;
+                }
+
+                @Override
+                public boolean isFallbackDefault() {
+                    return useDataSource != null && useDataSource.fallbackDefault();
+                }
+
+                @Override
+                public String getDataSourceId() {
+                    return useDataSource == null ? null : useDataSource.value();
+                }
+
+                @Override
+                public String toString() {
+                    return "Annotation Strategy(" + (useDataSource != null ? useDataSource : useDefaultDataSource) + ")";
+                }
+            };
+        }
+        return null;
+    }
+
+
+}

+ 66 - 0
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/strategy/CachedDataSourceSwitchStrategyMatcher.java

@@ -0,0 +1,66 @@
+package org.hswebframework.web.datasource.strategy;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.hswebframework.web.boost.aop.context.MethodInterceptorContext;
+import org.springframework.util.ClassUtils;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author zhouhao
+ * @since 3.0.0-RC
+ */
+@Slf4j
+public abstract class CachedDataSourceSwitchStrategyMatcher implements DataSourceSwitchStrategyMatcher {
+
+    static Map<CacheKey, Strategy> cache = new ConcurrentHashMap<>();
+
+    public abstract Strategy createStrategyIfMatch(Class target, Method method);
+
+    @Override
+    public boolean match(Class target, Method method) {
+        Strategy strategy = createStrategyIfMatch(target, method);
+        if (null != strategy) {
+            if (log.isDebugEnabled()) {
+                log.debug("create data source switcher strategy:{} for method:{}", strategy, method);
+            }
+            CacheKey cacheKey = new CacheKey(target, method);
+            cache.put(cacheKey, strategy);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public Strategy getStrategy(MethodInterceptorContext context) {
+        Method method = context.getMethod();
+        Class target = ClassUtils.getUserClass(context.getTarget());
+        return cache.get(new CacheKey(target, method));
+    }
+
+    @AllArgsConstructor
+    public static class CacheKey {
+
+        private Class target;
+
+        private Method method;
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof CacheKey)) {
+                return false;
+            }
+            CacheKey target = ((CacheKey) obj);
+            return target.target == this.target && target.method == method;
+        }
+
+        public int hashCode() {
+            int result = this.target != null ? this.target.hashCode() : 0;
+            result = 31 * result + (this.method != null ? this.method.hashCode() : 0);
+            return result;
+        }
+    }
+}

+ 60 - 0
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/strategy/DataSourceSwitchStrategyMatcher.java

@@ -0,0 +1,60 @@
+package org.hswebframework.web.datasource.strategy;
+
+import org.hswebframework.web.boost.aop.context.MethodInterceptorContext;
+import org.hswebframework.web.datasource.DynamicDataSource;
+import org.hswebframework.web.datasource.exception.DataSourceNotFoundException;
+
+import java.lang.reflect.Method;
+
+/**
+ * 数据源切换策略,可通过此接口来自定义数据源切换的方式
+ *
+ * @author zhouhao
+ * @since 3.0.0-RC
+ */
+public interface DataSourceSwitchStrategyMatcher {
+
+    /**
+     * 匹配类和方法,返回是否需要进行数据源切换
+     *
+     * @param target 类
+     * @param method 方法
+     * @return 是否需要进行数据源切换
+     */
+    boolean match(Class target, Method method);
+
+    /**
+     * 获取数据源切换策略
+     * @param context aop上下文
+     * @return 切换策略
+     */
+    Strategy getStrategy(MethodInterceptorContext context);
+
+    /**
+     * 数据源切换策略
+     */
+    interface Strategy {
+        /**
+         * 是否使用默认数据源,与 {@link this#getDataSourceId}互斥,只在{@link this#getDataSourceId}不为空时生效
+         *
+         * @return 是否使用默认数据源
+         */
+        boolean isUseDefaultDataSource();
+
+        /**
+         * 当数据源不存在时,是否回退为默认数据源,如果为false,当数据源不存在时,将会抛出异常{@link org.hswebframework.web.datasource.exception.DataSourceNotFoundException}
+         *
+         * @return 是否使用默认数据源
+         * @see DataSourceNotFoundException
+         */
+        boolean isFallbackDefault();
+
+        /**
+         * @return 要切换数据源的id
+         * @see DynamicDataSource#getId()
+         * @see org.hswebframework.web.datasource.switcher.DataSourceSwitcher#use(String)
+         */
+        String getDataSourceId();
+    }
+
+}

+ 67 - 0
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/strategy/ExpressionDataSourceSwitchStrategyMatcher.java

@@ -0,0 +1,67 @@
+package org.hswebframework.web.datasource.strategy;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.springframework.util.AntPathMatcher;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 表达式方式切换数据源,在配置文件中设置:
+ * <pre>
+ *     hsweb:
+ *        datasource:
+ *           switcher:
+ *              org.hswebframework.**.*Service.select*:
+ *                  data-source-id: test1
+ * </pre>
+ *
+ * @author zhouhao
+ * @since 3.0.0-RC
+ */
+public class ExpressionDataSourceSwitchStrategyMatcher extends CachedDataSourceSwitchStrategyMatcher {
+
+    @Getter
+    @Setter
+    private Map<String, ExpressionStrategy> switcher = new HashMap<>();
+
+    private static AntPathMatcher antPathMatcher = new AntPathMatcher(".");
+
+    @Override
+    public Strategy createStrategyIfMatch(Class target, Method method) {
+        if (switcher.isEmpty()) {
+            return null;
+        }
+        String text = target.getName().concat(".").concat(method.getName());
+
+        return switcher.entrySet().stream()
+                .filter(entry -> antPathMatcher.match(entry.getValue().getExpression(), text))
+                .peek(entry -> entry.getValue().setId(entry.getKey()))
+                .map(Map.Entry::getValue)
+                .findFirst()
+                .orElse(null);
+    }
+
+    @Getter
+    @Setter
+    public static class ExpressionStrategy implements Strategy {
+        private boolean useDefaultDataSource = false;
+        private boolean fallbackDefault      = false;
+        private String  dataSourceId         = null;
+        private String expression;
+        private String id;
+
+        public boolean isUseDefaultDataSource() {
+            return useDefaultDataSource && dataSourceId == null;
+        }
+
+        @Override
+        public String toString() {
+            return "Expression Strategy(use(" + (isUseDefaultDataSource() ? "default" : getDataSourceId()) + "),expression:" + getExpression() + ")";
+        }
+    }
+}

+ 0 - 1
hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/AtomikosDataSourceConfig.java

@@ -3,7 +3,6 @@ package org.hswebframework.web.datasource.jta;
 import com.atomikos.jdbc.AtomikosDataSourceBean;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
-import lombok.Getter;
 import lombok.extern.slf4j.Slf4j;
 import org.hswebframework.web.datasource.config.DynamicDataSourceConfig;
 

+ 12 - 14
hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/InMemoryAtomikosDataSourceRepository.java

@@ -1,5 +1,7 @@
 package org.hswebframework.web.datasource.jta;
 
+import lombok.Getter;
+import lombok.Setter;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 
 import javax.annotation.PostConstruct;
@@ -11,46 +13,42 @@ import java.util.Map;
 /**
  * @author zhouhao
  */
-@ConfigurationProperties(prefix = "hsweb.dynamic")
+@ConfigurationProperties(prefix = "hsweb.datasource")
 public class InMemoryAtomikosDataSourceRepository implements JtaDataSourceRepository {
-    private Map<String, AtomikosDataSourceConfig> datasource = new HashMap<>();
 
-    public Map<String, AtomikosDataSourceConfig> getDatasource() {
-        return datasource;
-    }
+    @Getter
+    @Setter
+    private Map<String, AtomikosDataSourceConfig> jta = new HashMap<>();
 
-    public void setDatasource(Map<String, AtomikosDataSourceConfig> datasource) {
-        this.datasource = datasource;
-    }
 
     @PostConstruct
     public void init() {
-        datasource.forEach((id, config) -> {
+        jta.forEach((id, config) -> {
             if (config.getId() == null) {
                 config.setId(id);
             } else if (!config.getId().equals(id)) {
-                datasource.put(config.getId(), config);
+                jta.put(config.getId(), config);
             }
         });
     }
 
     @Override
     public List<AtomikosDataSourceConfig> findAll() {
-        return new ArrayList<>(datasource.values());
+        return new ArrayList<>(jta.values());
     }
 
     @Override
     public AtomikosDataSourceConfig findById(String dataSourceId) {
-        return datasource.get(dataSourceId);
+        return jta.get(dataSourceId);
     }
 
     @Override
     public AtomikosDataSourceConfig add(AtomikosDataSourceConfig config) {
-        return datasource.put(config.getId(), config);
+        return jta.put(config.getId(), config);
     }
 
     @Override
     public AtomikosDataSourceConfig remove(String dataSourceId) {
-        return datasource.remove(dataSourceId);
+        return jta.remove(dataSourceId);
     }
 }

+ 45 - 49
hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/JtaDynamicDataSourceService.java

@@ -1,5 +1,6 @@
 package org.hswebframework.web.datasource.jta;
 
+import lombok.SneakyThrows;
 import org.hswebframework.web.datasource.DynamicDataSource;
 import org.hswebframework.web.datasource.DynamicDataSourceProxy;
 import org.hswebframework.web.datasource.config.DynamicDataSourceConfigRepository;
@@ -44,9 +45,8 @@ public class JtaDynamicDataSourceService extends AbstractDynamicDataSourceServic
     }
 
 
-
-
     @Override
+    @SneakyThrows
     protected DataSourceCache createCache(AtomikosDataSourceConfig config) {
         AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
         config.putProperties(atomikosDataSourceBean);
@@ -54,57 +54,53 @@ public class JtaDynamicDataSourceService extends AbstractDynamicDataSourceServic
         atomikosDataSourceBean.setUniqueResourceName("dynamic_ds_" + config.getId());
         AtomicInteger successCounter = new AtomicInteger();
         CountDownLatch downLatch = new CountDownLatch(1);
-        try {
-            DataSourceCache cache = new DataSourceCache(config.hashCode(), new DynamicDataSourceProxy(config.getId(), atomikosDataSourceBean), downLatch, config) {
-                @Override
-                public void closeDataSource() {
-                    super.closeDataSource();
-                    atomikosDataSourceBean.close();
-                    XADataSource dataSource = atomikosDataSourceBean.getXaDataSource();
-                    if (dataSource instanceof Closeable) {
-                        try {
-                            ((Closeable) dataSource).close();
-                        } catch (IOException e) {
-                            logger.error("close xa datasource error", e);
-                        }
-                    } else {
-                        logger.warn("XADataSource is not instanceof Closeable!", (Object) Thread.currentThread().getStackTrace());
+        DataSourceCache cache = new DataSourceCache(config.hashCode(), new DynamicDataSourceProxy(config.getId(), atomikosDataSourceBean), downLatch, config) {
+            @Override
+            public void closeDataSource() {
+                super.closeDataSource();
+                atomikosDataSourceBean.close();
+                XADataSource dataSource = atomikosDataSourceBean.getXaDataSource();
+                if (dataSource instanceof Closeable) {
+                    try {
+                        ((Closeable) dataSource).close();
+                    } catch (IOException e) {
+                        logger.error("close xa datasource error", e);
                     }
+                } else {
+                    logger.warn("XADataSource is not instanceof Closeable!", (Object) Thread.currentThread().getStackTrace());
                 }
-            };
-            //异步初始化
-            executor.execute(() -> {
-                try {
-                    atomikosDataSourceBean.init();
-                    successCounter.incrementAndGet();
-                    downLatch.countDown();
-                } catch (Exception e) {
-                    logger.error("init datasource {} error", config.getId(), e);
+            }
+        };
+        //异步初始化
+        executor.execute(() -> {
+            try {
+                atomikosDataSourceBean.init();
+                successCounter.incrementAndGet();
+                downLatch.countDown();
+            } catch (Exception e) {
+                logger.error("init datasource {} error", config.getId(), e);
 
-                    //atomikosDataSourceBean.close();
-                }
-            });
-            //初始化状态判断
-            executor.execute(() -> {
-                try {
-                    Thread.sleep(config.getInitTimeout() * 1000L);
-                } catch (InterruptedException ignored) {
-                    logger.warn(ignored.getMessage(), ignored);
-                    Thread.currentThread().interrupt();
-                } finally {
-                    if (successCounter.get() == 0) {
-                        // 初始化超时,认定为失败
-                        logger.error("init timeout ({}ms)", config.getInitTimeout());
-                        cache.closeDataSource();
-                        if (downLatch.getCount() > 0) {
-                            downLatch.countDown();
-                        }
+                //atomikosDataSourceBean.close();
+            }
+        });
+        //初始化状态判断
+        executor.execute(() -> {
+            try {
+                Thread.sleep(config.getInitTimeout() * 1000L);
+            } catch (InterruptedException ignored) {
+                logger.warn(ignored.getMessage(), ignored);
+                Thread.currentThread().interrupt();
+            } finally {
+                if (successCounter.get() == 0) {
+                    // 初始化超时,认定为失败
+                    logger.error("init timeout ({}ms)", config.getInitTimeout());
+                    cache.closeDataSource();
+                    if (downLatch.getCount() > 0) {
+                        downLatch.countDown();
                     }
                 }
-            });
-            return cache;
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
+            }
+        });
+        return cache;
     }
 }

+ 30 - 3
hsweb-datasource/hsweb-datasource-jta/src/test/java/org/hswebframework/web/datasource/jta/SimpleAtomikosTests.java

@@ -16,6 +16,8 @@ import org.hswebframework.ezorm.rdb.render.dialect.OracleRDBDatabaseMetaData;
 import org.hswebframework.ezorm.rdb.simple.SimpleDatabase;
 import org.hswebframework.web.datasource.DataSourceHolder;
 import org.hswebframework.web.datasource.DatabaseType;
+import org.hswebframework.web.datasource.annotation.UseDataSource;
+import org.hswebframework.web.datasource.exception.DataSourceNotFoundException;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -63,9 +65,9 @@ public class SimpleAtomikosTests extends AbstractTransactionalJUnit4SpringContex
         }
 
         public class DynDatabaseMeta extends RDBDatabaseMetaData {
-            private Map<DatabaseType, Dialect> dialectMap;
+            private Map<DatabaseType, Dialect>             dialectMap;
             private Map<DatabaseType, RDBDatabaseMetaData> metaDataMap;
-            private Map<DatabaseType, TableMetaParser> parserMap;
+            private Map<DatabaseType, TableMetaParser>     parserMap;
 
             public DynDatabaseMeta(SqlExecutor sqlExecutor) {
                 dialectMap = new HashMap<>();
@@ -150,6 +152,18 @@ public class SimpleAtomikosTests extends AbstractTransactionalJUnit4SpringContex
         Assert.assertNull(DataSourceHolder.switcher().currentDataSourceId());
         Assert.assertTrue(dynDsTest.testQuery().size() > 0);
 
+        dynDsTest.findAll();
+
+        dynDsTest.query();
+
+        dynDsTest.query();
+
+        try {
+            dynDsTest.query("test123");
+            Assert.assertTrue(false);
+        } catch (DataSourceNotFoundException e) {
+        }
+
         jmsTemplate.convertAndSend("test", "hello");
         Thread.sleep(1000);
     }
@@ -177,10 +191,23 @@ public class SimpleAtomikosTests extends AbstractTransactionalJUnit4SpringContex
                     .exec();
         }
 
-
         public List testQuery() throws SQLException {
             return database.getTable("s_user").createQuery().list();
         }
+
+        public List findAll() throws SQLException {
+            return database.getTable("s_user").createQuery().list();
+        }
+
+        @UseDataSource("test_ds")
+        public List query() throws SQLException {
+            return database.getTable("s_user").createQuery().list();
+        }
+
+        @UseDataSource("${#dataSourceId}")
+        public List query(String dataSourceId) throws SQLException {
+            return database.getTable("s_user").createQuery().list();
+        }
     }
 
 }

+ 8 - 2
hsweb-datasource/hsweb-datasource-jta/src/test/resources/application.yml

@@ -23,8 +23,8 @@ logging:
       com.atomikos: WARN
       org.hswebframework: DEBUG
 hsweb:
-  dynamic:
-    datasource:
+  datasource:
+    jta:
         test_ds:
             xa-data-source-class-name: com.alibaba.druid.pool.xa.DruidXADataSource
             xa-properties:
@@ -43,6 +43,12 @@ hsweb:
           max-pool-size: 20
           borrow-connection-timeout: 1000
           init-timeout: 20
+    switcher:
+       test: # 只是一个标识
+          # 拦截表达式
+          expression: org.hswebframework.**.*DynDsTest.find*
+          # 使用数据源
+          data-source-id: test_ds
 
 config:
     test: 123