瀏覽代碼

优化动态数据源

zhou-hao 7 年之前
父節點
當前提交
d88b80b55a
共有 26 個文件被更改,包括 395 次插入270 次删除
  1. 5 0
      hsweb-datasource/hsweb-datasource-api/pom.xml
  2. 2 2
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/DataSourceHolder.java
  3. 1 0
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/DynamicDataSource.java
  4. 22 28
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/DynamicDataSourceAutoConfiguration.java
  5. 16 9
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/DynamicDataSourceProxy.java
  6. 14 0
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/config/DynamicDataSourceConfig.java
  7. 13 0
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/config/DynamicDataSourceConfigRepository.java
  8. 12 0
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/config/InSpringDynamicDataSourceConfig.java
  9. 4 3
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/exception/DataSourceClosedException.java
  10. 4 1
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/exception/DataSourceNotFoundException.java
  11. 33 8
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/service/AbstractDynamicDataSourceService.java
  12. 9 1
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/service/DataSourceCache.java
  13. 38 15
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/service/InMemoryDynamicDataSourceService.java
  14. 54 0
      hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/service/InSpringDynamicDataSourceConfigRepository.java
  15. 2 2
      hsweb-datasource/hsweb-datasource-jta/README.md
  16. 6 4
      hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/AtomikosDataSourceAutoConfiguration.java
  17. 8 134
      hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/AtomikosDataSourceConfig.java
  18. 56 0
      hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/InMemoryAtomikosDataSourceRepository.java
  19. 4 5
      hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/JtaDataSourceRepository.java
  20. 16 29
      hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/JtaDynamicDataSourceService.java
  21. 0 29
      hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/MemoryJtaDataSourceRepository.java
  22. 26 0
      hsweb-datasource/hsweb-datasource-web/pom.xml
  23. 33 0
      hsweb-datasource/hsweb-datasource-web/src/main/java/org/hswebframework/web/datasource/web/DatasourceController.java
  24. 13 0
      hsweb-datasource/hsweb-datasource-web/src/main/java/org/hswebframework/web/datasource/web/DatasourceWebApiAutoConfiguration.java
  25. 3 0
      hsweb-datasource/hsweb-datasource-web/src/main/resources/META-INF/spring.factories
  26. 1 0
      hsweb-datasource/pom.xml

+ 5 - 0
hsweb-datasource/hsweb-datasource-api/pom.xml

@@ -13,6 +13,11 @@
 
 
     <dependencies>
+        <dependency>
+            <groupId>org.hswebframework.web</groupId>
+            <artifactId>hsweb-core</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.hswebframework.web</groupId>
             <artifactId>hsweb-commons-utils</artifactId>

+ 2 - 2
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/DataSourceHolder.java

@@ -17,11 +17,11 @@ public final class DataSourceHolder {
     /**
      * 动态数据源切换器
      */
-    static DataSourceSwitcher dataSourceSwitcher = defaultSwitcher;
+    static volatile DataSourceSwitcher dataSourceSwitcher = defaultSwitcher;
     /**
      * 动态数据源服务
      */
-    static DynamicDataSourceService dynamicDataSourceService;
+    static volatile DynamicDataSourceService dynamicDataSourceService;
 
     public static void checkDynamicDataSourceReady() {
         if (dynamicDataSourceService == null) {

+ 1 - 0
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/DynamicDataSource.java

@@ -28,4 +28,5 @@ public interface DynamicDataSource {
      * @return 原始数据源
      */
     DataSource getNative();
+
 }

+ 22 - 28
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/DynamicDataSourceAutoConfiguration.java

@@ -1,7 +1,10 @@
 package org.hswebframework.web.datasource;
 
 import org.hswebframework.ezorm.rdb.executor.SqlExecutor;
+import org.hswebframework.web.datasource.config.DynamicDataSourceConfigRepository;
+import org.hswebframework.web.datasource.config.InSpringDynamicDataSourceConfig;
 import org.hswebframework.web.datasource.service.InMemoryDynamicDataSourceService;
+import org.hswebframework.web.datasource.service.InSpringDynamicDataSourceConfigRepository;
 import org.hswebframework.web.datasource.starter.AopDataSourceSwitcherAutoConfiguration;
 import org.hswebframework.web.datasource.switcher.DataSourceSwitcher;
 import org.springframework.beans.BeansException;
@@ -28,11 +31,17 @@ public class DynamicDataSourceAutoConfiguration implements BeanPostProcessor {
         return new DefaultJdbcExecutor();
     }
 
+    @Bean
+    @ConditionalOnMissingBean(DynamicDataSourceConfigRepository.class)
+    public InSpringDynamicDataSourceConfigRepository inSpringDynamicDataSourceConfigRepository() {
+        return new InSpringDynamicDataSourceConfigRepository();
+    }
+
     @Bean
     @ConditionalOnMissingBean(DynamicDataSourceService.class)
-    public InMemoryDynamicDataSourceService inMemoryDynamicDataSourceService(DataSource dataSource) {
+    public InMemoryDynamicDataSourceService inMemoryDynamicDataSourceService(DynamicDataSourceConfigRepository<InSpringDynamicDataSourceConfig> repository, DataSource dataSource) {
         DynamicDataSourceProxy dataSourceProxy = new DynamicDataSourceProxy(null, dataSource);
-        return new InMemoryDynamicDataSourceService(dataSourceProxy);
+        return new InMemoryDynamicDataSourceService(repository, dataSourceProxy);
     }
 
     @Override
@@ -40,32 +49,6 @@ public class DynamicDataSourceAutoConfiguration implements BeanPostProcessor {
         return bean;
     }
 
-    @ConditionalOnBean(InMemoryDynamicDataSourceService.class)
-    @Configuration
-    public static class AutoRegisterDataSource implements BeanPostProcessor {
-
-        private InMemoryDynamicDataSourceService dataSourceService;
-
-        @Autowired
-        public void setDataSourceService(InMemoryDynamicDataSourceService dataSourceService) {
-            DataSourceHolder.dynamicDataSourceService = dataSourceService;
-            this.dataSourceService = dataSourceService;
-        }
-
-        @Override
-        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
-            return bean;
-        }
-
-        @Override
-        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
-            if (bean instanceof DataSource) {
-                dataSourceService.registerDataSource(beanName, ((DataSource) bean));
-            }
-            return bean;
-        }
-    }
-
     @Override
     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
         if (bean instanceof DynamicDataSourceService) {
@@ -76,4 +59,15 @@ public class DynamicDataSourceAutoConfiguration implements BeanPostProcessor {
         }
         return bean;
     }
+
+
+    @Configuration
+    public static class AutoRegisterDataSource {
+        @Autowired
+        public void setDataSourceService(DynamicDataSourceService dataSourceService) {
+            DataSourceHolder.dynamicDataSourceService = dataSourceService;
+        }
+    }
+
+
 }

+ 16 - 9
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/DynamicDataSourceProxy.java

@@ -5,6 +5,8 @@ import java.io.PrintWriter;
 import java.sql.Connection;
 import java.sql.SQLException;
 import java.sql.SQLFeatureNotSupportedException;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.logging.Logger;
 
 /**
@@ -21,6 +23,8 @@ public class DynamicDataSourceProxy implements DynamicDataSource {
 
     private DataSource proxy;
 
+    private Lock lock = new ReentrantLock();
+
     public DynamicDataSourceProxy(String id, DatabaseType databaseType, DataSource proxy) {
         this.id = id;
         this.databaseType = databaseType;
@@ -45,19 +49,22 @@ public class DynamicDataSourceProxy implements DynamicDataSource {
     @Override
     public DatabaseType getType() {
         if (databaseType == null) {
-            synchronized (this) {
-                if (databaseType == null) {
-                    try {
-                        try (Connection connection = proxy.getConnection()) {
-                            databaseType = DatabaseType.fromJdbcUrl(connection.getMetaData().getURL());
-                        }
-                    } catch (SQLException e) {
-                        throw new RuntimeException(e);
-                    }
+            lock.lock();
+            if (databaseType != null) {
+                return databaseType;
+            }
+            try {
+                try (Connection connection = proxy.getConnection()) {
+                    databaseType = DatabaseType.fromJdbcUrl(connection.getMetaData().getURL());
                 }
+            } catch (SQLException e) {
+                throw new UnsupportedOperationException(e);
+            } finally {
+                lock.unlock();
             }
         }
 
         return databaseType;
     }
+
 }

+ 14 - 0
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/config/DynamicDataSourceConfig.java

@@ -0,0 +1,14 @@
+package org.hswebframework.web.datasource.config;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class DynamicDataSourceConfig implements Serializable {
+    private String id;
+
+    private String name;
+
+    private String describe;
+}

+ 13 - 0
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/config/DynamicDataSourceConfigRepository.java

@@ -0,0 +1,13 @@
+package org.hswebframework.web.datasource.config;
+
+import java.util.List;
+
+public interface DynamicDataSourceConfigRepository<C extends DynamicDataSourceConfig> {
+    List<C> findAll();
+
+    C findById(String dataSourceId);
+
+    C add(C config);
+
+    C remove(String dataSourceId);
+}

+ 12 - 0
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/config/InSpringDynamicDataSourceConfig.java

@@ -0,0 +1,12 @@
+package org.hswebframework.web.datasource.config;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class InSpringDynamicDataSourceConfig extends DynamicDataSourceConfig {
+    private static final long serialVersionUID = -8434216403009495774L;
+
+    private String beanName;
+}

+ 4 - 3
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/exception/DataSourceClosedException.java

@@ -1,12 +1,13 @@
 package org.hswebframework.web.datasource.exception;
 
+import org.hswebframework.web.NotFoundException;
+
 /**
- * TODO 完成注释
- *
  * @author zhouhao
  */
-public class DataSourceClosedException extends RuntimeException {
+public class DataSourceClosedException extends NotFoundException {
 
+    private static final long serialVersionUID = 7474086353335778733L;
     private String dataSourceId;
 
     public DataSourceClosedException(String dataSourceId) {

+ 4 - 1
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/exception/DataSourceNotFoundException.java

@@ -1,10 +1,13 @@
 package org.hswebframework.web.datasource.exception;
 
+import org.hswebframework.web.NotFoundException;
+
 /**
  * @author zhouhao
  */
-public class DataSourceNotFoundException extends RuntimeException {
+public class DataSourceNotFoundException extends NotFoundException {
 
+    private static final long serialVersionUID = -8750742814977236806L;
     private String dataSourceId;
 
     public DataSourceNotFoundException(String dataSourceId) {

+ 33 - 8
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/service/AbstractDynamicDataSourceService.java

@@ -3,27 +3,39 @@ package org.hswebframework.web.datasource.service;
 import org.hswebframework.web.datasource.DynamicDataSource;
 import org.hswebframework.web.datasource.DynamicDataSourceProxy;
 import org.hswebframework.web.datasource.DynamicDataSourceService;
+import org.hswebframework.web.datasource.config.DynamicDataSourceConfig;
+import org.hswebframework.web.datasource.config.DynamicDataSourceConfigRepository;
+import org.hswebframework.web.datasource.exception.DataSourceNotFoundException;
 
 import javax.annotation.PreDestroy;
 import javax.sql.DataSource;
 import java.sql.SQLException;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
 
 /**
  * @author zhouhao
  */
-public abstract class AbstractDynamicDataSourceService implements DynamicDataSourceService {
+public abstract class AbstractDynamicDataSourceService<C extends DynamicDataSourceConfig> implements DynamicDataSourceService {
     protected final Map<String, DataSourceCache> dataSourceStore = new ConcurrentHashMap<>(32);
 
     private final DynamicDataSource defaultDataSource;
 
-    public AbstractDynamicDataSourceService(DynamicDataSource defaultDataSource) {
+    private DynamicDataSourceConfigRepository<C> repository;
+
+    public void setRepository(DynamicDataSourceConfigRepository<C> repository) {
+        this.repository = repository;
+    }
+
+    public AbstractDynamicDataSourceService(DynamicDataSourceConfigRepository<C> repository, DynamicDataSource defaultDataSource) {
         this.defaultDataSource = defaultDataSource;
+        this.repository = repository;
     }
 
-    public AbstractDynamicDataSourceService(DataSource dataSource) throws SQLException {
-        this(dataSource instanceof DynamicDataSource ? (DynamicDataSource) dataSource : new DynamicDataSourceProxy(null, dataSource));
+    public AbstractDynamicDataSourceService(DynamicDataSourceConfigRepository<C> repository, DataSource dataSource) throws SQLException {
+        this(repository, new DynamicDataSourceProxy(null, dataSource));
     }
 
     @PreDestroy
@@ -34,12 +46,17 @@ public abstract class AbstractDynamicDataSourceService implements DynamicDataSou
     @Override
     public DynamicDataSource getDataSource(String dataSourceId) {
         DataSourceCache cache = dataSourceStore.get(dataSourceId);
+        C config = repository.findById(dataSourceId);
+        if (config == null) {
+            throw new DataSourceNotFoundException(dataSourceId, "数据源" + dataSourceId + "不存在");
+
+        }
         if (cache == null) {
-            cache = createCache(dataSourceId);
+            cache = createCache(config);
             dataSourceStore.put(dataSourceId, cache);
             return cache.getDataSource();
         }
-        if (cache.getHash() != getHash(dataSourceId)) {
+        if (cache.getHash() != config.hashCode()) {
             dataSourceStore.remove(dataSourceId);
             cache.closeDataSource();
             //重新获取
@@ -53,9 +70,17 @@ public abstract class AbstractDynamicDataSourceService implements DynamicDataSou
         return defaultDataSource;
     }
 
-    protected abstract int getHash(String id);
+    @Deprecated
+    protected int getHash(String id) {
+        return -1;
+    }
+
+    protected abstract DataSourceCache createCache(C config);
 
-    protected abstract DataSourceCache createCache(String id);
+    @Deprecated
+    protected DataSourceCache createCache(String id) {
+        return null;
+    }
 
     public DataSourceCache removeCache(String id) {
         return dataSourceStore.remove(id);

+ 9 - 1
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/service/DataSourceCache.java

@@ -2,6 +2,7 @@ package org.hswebframework.web.datasource.service;
 
 import lombok.extern.slf4j.Slf4j;
 import org.hswebframework.web.datasource.DynamicDataSource;
+import org.hswebframework.web.datasource.config.DynamicDataSourceConfig;
 import org.hswebframework.web.datasource.exception.DataSourceClosedException;
 
 import java.util.concurrent.CountDownLatch;
@@ -25,6 +26,8 @@ public class DataSourceCache {
         return hash;
     }
 
+    private DynamicDataSourceConfig config;
+
     public DynamicDataSource getDataSource() {
         if (initLatch != null) {
             try {
@@ -43,10 +46,11 @@ public class DataSourceCache {
         return dataSource;
     }
 
-    public DataSourceCache(long hash, DynamicDataSource dataSource, CountDownLatch initLatch) {
+    public DataSourceCache(long hash, DynamicDataSource dataSource, CountDownLatch initLatch,DynamicDataSourceConfig config) {
         this.hash = hash;
         this.dataSource = dataSource;
         this.initLatch = initLatch;
+        this.config=config;
     }
 
     public boolean isClosed() {
@@ -57,4 +61,8 @@ public class DataSourceCache {
     public void closeDataSource() {
         closed = true;
     }
+
+    public DynamicDataSourceConfig getConfig() {
+        return config;
+    }
 }

+ 38 - 15
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/service/InMemoryDynamicDataSourceService.java

@@ -2,43 +2,66 @@ package org.hswebframework.web.datasource.service;
 
 import org.hswebframework.web.datasource.DynamicDataSource;
 import org.hswebframework.web.datasource.DynamicDataSourceProxy;
+import org.hswebframework.web.datasource.config.DynamicDataSourceConfig;
+import org.hswebframework.web.datasource.config.DynamicDataSourceConfigRepository;
+import org.hswebframework.web.datasource.config.InSpringDynamicDataSourceConfig;
 import org.hswebframework.web.datasource.exception.DataSourceNotFoundException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
 
 import javax.sql.DataSource;
+import java.sql.SQLException;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CountDownLatch;
+import java.util.stream.Collectors;
 
 /**
  * @author zhouhao
  * @since 1.0
  */
-public class InMemoryDynamicDataSourceService extends AbstractDynamicDataSourceService {
-    public InMemoryDynamicDataSourceService(DynamicDataSource defaultDataSource) {
-        super(defaultDataSource);
+public class InMemoryDynamicDataSourceService extends AbstractDynamicDataSourceService<InSpringDynamicDataSourceConfig> {
+
+
+    @Autowired
+    private ApplicationContext applicationContext;
+
+    public void setApplicationContext(ApplicationContext applicationContext) {
+        this.applicationContext = applicationContext;
     }
 
-    private Map<String, DataSourceCache> dataSourceMap = new HashMap<>();
+    public InMemoryDynamicDataSourceService(DynamicDataSourceConfigRepository<InSpringDynamicDataSourceConfig> repository, DynamicDataSource defaultDataSource) {
+        super(repository, defaultDataSource);
+    }
+
+    public InMemoryDynamicDataSourceService(DynamicDataSourceConfigRepository<InSpringDynamicDataSourceConfig> repository, DataSource dataSource) throws SQLException {
+        super(repository, dataSource);
+    }
 
+    @Deprecated
     public void registerDataSource(String id, DataSource dataSource) {
+        InSpringDynamicDataSourceConfig config = new InSpringDynamicDataSourceConfig();
+        config.setId(id);
+        config.setName(id);
         CountDownLatch countDownLatch = new CountDownLatch(1);
-        dataSourceMap.put(id, new DataSourceCache(0L
-                , new DynamicDataSourceProxy(id, dataSource), countDownLatch));
+        dataSourceStore.put(id, new DataSourceCache(0L
+                , new DynamicDataSourceProxy(id, dataSource), countDownLatch, config));
         countDownLatch.countDown();
     }
 
     @Override
-    protected int getHash(String id) {
-        return 0;
-    }
+    protected DataSourceCache createCache(InSpringDynamicDataSourceConfig config) {
+        DataSource dataSource = applicationContext.getBean(config.getBeanName(), DataSource.class);
+        CountDownLatch countDownLatch = new CountDownLatch(1);
+        try {
+            return new DataSourceCache(config.hashCode(), new DynamicDataSourceProxy(config.getId(), dataSource), countDownLatch, config);
 
-    @Override
-    protected DataSourceCache createCache(String id) {
-        DataSourceCache cache = dataSourceMap.get(id);
-        if (cache == null) {
-            throw new DataSourceNotFoundException(id);
+        } finally {
+            countDownLatch.countDown();
         }
-        return cache;
+
     }
 
+
 }

+ 54 - 0
hsweb-datasource/hsweb-datasource-api/src/main/java/org/hswebframework/web/datasource/service/InSpringDynamicDataSourceConfigRepository.java

@@ -0,0 +1,54 @@
+package org.hswebframework.web.datasource.service;
+
+import org.hswebframework.web.datasource.config.DynamicDataSourceConfigRepository;
+import org.hswebframework.web.datasource.config.InSpringDynamicDataSourceConfig;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+
+import javax.sql.DataSource;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class InSpringDynamicDataSourceConfigRepository implements DynamicDataSourceConfigRepository<InSpringDynamicDataSourceConfig>, BeanPostProcessor {
+
+    private Map<String, InSpringDynamicDataSourceConfig> configMap = new HashMap<>();
+
+    @Override
+    public List<InSpringDynamicDataSourceConfig> findAll() {
+        return new ArrayList<>(configMap.values());
+    }
+
+    @Override
+    public InSpringDynamicDataSourceConfig findById(String dataSourceId) {
+        return configMap.get(dataSourceId);
+    }
+
+    @Override
+    public InSpringDynamicDataSourceConfig add(InSpringDynamicDataSourceConfig config) {
+        return configMap.put(config.getId(), config);
+    }
+
+    @Override
+    public InSpringDynamicDataSourceConfig remove(String dataSourceId) {
+        return configMap.remove(dataSourceId);
+    }
+
+    @Override
+    public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
+        return o;
+    }
+
+    @Override
+    public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
+        if (o instanceof DataSource) {
+            InSpringDynamicDataSourceConfig config = new InSpringDynamicDataSourceConfig();
+            config.setId(s);
+            config.setBeanName(s);
+            config.setName(s);
+            add(config);
+        }
+        return o;
+    }
+}

+ 2 - 2
hsweb-datasource/hsweb-datasource-jta/README.md

@@ -21,7 +21,7 @@ spring:
         local-transaction-mode: true
 ```
 
-动态数据源配置,默认提供一个 ``MemoryJtaDataSourceStore``,在application.yml 中进行配置即可:
+动态数据源配置,默认提供一个 ``InMemoryAtomikosDataSourceRepository``,在application.yml 中进行配置即可:
 ```yaml
 hsweb:
   dynamic:
@@ -46,4 +46,4 @@ hsweb:
           init-timeout: 20
 ```
 
-自定义,将数据源配置放到数据库中,实现 ``JtaDataSourceStore`` 接口并注入到spring容器即可
+自定义,将数据源配置放到数据库中,实现 ``DynamicDataSourceConfigRepository<AtomikosDataSourceConfig>`` 接口并注入到spring容器即可

+ 6 - 4
hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/AtomikosDataSourceAutoConfiguration.java

@@ -1,5 +1,6 @@
 package org.hswebframework.web.datasource.jta;
 
+import org.hswebframework.web.datasource.config.DynamicDataSourceConfigRepository;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
 import org.springframework.context.annotation.Bean;
@@ -24,13 +25,14 @@ public class AtomikosDataSourceAutoConfiguration {
 
     @ConditionalOnMissingBean(JtaDataSourceRepository.class)
     @Bean
-    public MemoryJtaDataSourceRepository memoryJtaDataSourceStore() {
-        return new MemoryJtaDataSourceRepository();
+    public InMemoryAtomikosDataSourceRepository memoryJtaDataSourceStore() {
+        return new InMemoryAtomikosDataSourceRepository();
     }
 
     @Bean
-    public JtaDynamicDataSourceService jtaDynamicDataSourceService(JtaDataSourceRepository jtaDataSourceRepository, DataSource dataSource) throws SQLException {
-        return new JtaDynamicDataSourceService(jtaDataSourceRepository, dataSource);
+    public JtaDynamicDataSourceService jtaDynamicDataSourceService(DynamicDataSourceConfigRepository<AtomikosDataSourceConfig> repository
+            , DataSource dataSource) throws SQLException {
+        return new JtaDynamicDataSourceService(repository, dataSource);
     }
 
 }

+ 8 - 134
hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/AtomikosDataSourceConfig.java

@@ -1,7 +1,11 @@
 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;
 
 import java.sql.SQLException;
 import java.util.Properties;
@@ -9,8 +13,11 @@ import java.util.Properties;
 /**
  * @author zhouhao
  */
+@EqualsAndHashCode(callSuper = true)
 @Slf4j
-public class AtomikosDataSourceConfig {
+@Data
+public class AtomikosDataSourceConfig extends DynamicDataSourceConfig{
+    private static final long serialVersionUID = 5588085000663972571L;
     private int minPoolSize = 5;
     private int maxPoolSize = 200;
     private int borrowConnectionTimeout = 60;
@@ -26,139 +33,6 @@ public class AtomikosDataSourceConfig {
     //初始化超时时间
     private int initTimeout = 10;
 
-    @Override
-    public int hashCode() {
-        return toString().hashCode();
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        return obj instanceof AtomikosDataSourceConfig && hashCode() == obj.hashCode();
-    }
-
-    @Override
-    public String toString() {
-        return "AtomikosDataSourceConfig{" +
-                "minPoolSize=" + minPoolSize +
-                ", maxPoolSize=" + maxPoolSize +
-                ", borrowConnectionTimeout=" + borrowConnectionTimeout +
-                ", reapTimeout=" + reapTimeout +
-                ", maxIdleTime=" + maxIdleTime +
-                ", maintenanceInterval=" + maintenanceInterval +
-                ", defaultIsolationLevel=" + defaultIsolationLevel +
-                ", xaDataSourceClassName='" + xaDataSourceClassName + '\'' +
-                ", loginTimeout=" + loginTimeout +
-                ", testQuery='" + testQuery + '\'' +
-                ", maxLifetime=" + maxLifetime +
-                ", xaProperties=" + xaProperties +
-                ", initTimeout=" + initTimeout +
-                '}';
-    }
-
-    public Properties getXaProperties() {
-        return xaProperties;
-    }
-
-    public void setXaProperties(Properties xaProperties) {
-        this.xaProperties = xaProperties;
-    }
-
-    public int getMinPoolSize() {
-        return minPoolSize;
-    }
-
-    public void setMinPoolSize(int minPoolSize) {
-        this.minPoolSize = minPoolSize;
-    }
-
-    public int getMaxPoolSize() {
-        return maxPoolSize;
-    }
-
-    public void setMaxPoolSize(int maxPoolSize) {
-        this.maxPoolSize = maxPoolSize;
-    }
-
-    public int getBorrowConnectionTimeout() {
-        return borrowConnectionTimeout;
-    }
-
-    public void setBorrowConnectionTimeout(int borrowConnectionTimeout) {
-        this.borrowConnectionTimeout = borrowConnectionTimeout;
-    }
-
-    public int getReapTimeout() {
-        return reapTimeout;
-    }
-
-    public void setReapTimeout(int reapTimeout) {
-        this.reapTimeout = reapTimeout;
-    }
-
-    public int getMaxIdleTime() {
-        return maxIdleTime;
-    }
-
-    public void setMaxIdleTime(int maxIdleTime) {
-        this.maxIdleTime = maxIdleTime;
-    }
-
-    public int getMaintenanceInterval() {
-        return maintenanceInterval;
-    }
-
-    public void setMaintenanceInterval(int maintenanceInterval) {
-        this.maintenanceInterval = maintenanceInterval;
-    }
-
-    public int getDefaultIsolationLevel() {
-        return defaultIsolationLevel;
-    }
-
-    public void setDefaultIsolationLevel(int defaultIsolationLevel) {
-        this.defaultIsolationLevel = defaultIsolationLevel;
-    }
-
-    public String getXaDataSourceClassName() {
-        return xaDataSourceClassName;
-    }
-
-    public void setXaDataSourceClassName(String xaDataSourceClassName) {
-        this.xaDataSourceClassName = xaDataSourceClassName;
-    }
-
-    public int getLoginTimeout() {
-        return loginTimeout;
-    }
-
-    public void setLoginTimeout(int loginTimeout) {
-        this.loginTimeout = loginTimeout;
-    }
-
-    public String getTestQuery() {
-        return testQuery;
-    }
-
-    public void setTestQuery(String testQuery) {
-        this.testQuery = testQuery;
-    }
-
-    public int getMaxLifetime() {
-        return maxLifetime;
-    }
-
-    public void setMaxLifetime(int maxLifetime) {
-        this.maxLifetime = maxLifetime;
-    }
-
-    public int getInitTimeout() {
-        return initTimeout;
-    }
-
-    public void setInitTimeout(int initTimeout) {
-        this.initTimeout = initTimeout;
-    }
-
     public void putProperties(AtomikosDataSourceBean atomikosDataSourceBean) {
         if (null != xaProperties) {
             xaProperties.entrySet().forEach(entry -> entry.setValue(String.valueOf(entry.getValue())));

+ 56 - 0
hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/InMemoryAtomikosDataSourceRepository.java

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

+ 4 - 5
hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/JtaDataSourceRepository.java

@@ -1,12 +1,11 @@
 package org.hswebframework.web.datasource.jta;
 
+import org.hswebframework.web.datasource.config.DynamicDataSourceConfigRepository;
+
 /**
- * TODO 完成注释
- *
  * @author zhouhao
+ * @see org.hswebframework.web.datasource.config.DynamicDataSourceConfigRepository
  */
-public interface JtaDataSourceRepository {
-
-    AtomikosDataSourceConfig getConfig(String id);
+public interface JtaDataSourceRepository extends DynamicDataSourceConfigRepository<AtomikosDataSourceConfig> {
 
 }

+ 16 - 29
hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/JtaDynamicDataSourceService.java

@@ -2,6 +2,7 @@ package org.hswebframework.web.datasource.jta;
 
 import org.hswebframework.web.datasource.DynamicDataSource;
 import org.hswebframework.web.datasource.DynamicDataSourceProxy;
+import org.hswebframework.web.datasource.config.DynamicDataSourceConfigRepository;
 import org.hswebframework.web.datasource.exception.DataSourceNotFoundException;
 import org.hswebframework.web.datasource.service.AbstractDynamicDataSourceService;
 import org.hswebframework.web.datasource.service.DataSourceCache;
@@ -23,52 +24,38 @@ import java.util.concurrent.atomic.AtomicInteger;
 /**
  * @author zhouhao
  */
-public class JtaDynamicDataSourceService extends AbstractDynamicDataSourceService {
+public class JtaDynamicDataSourceService extends AbstractDynamicDataSourceService<AtomikosDataSourceConfig> {
 
-    private JtaDataSourceRepository jtaDataSourceRepository;
-
-    private Executor executor = Executors.newCachedThreadPool();
+    private Executor executor = Executors.newFixedThreadPool(4);
 
     private Logger logger = LoggerFactory.getLogger(this.getClass());
 
+    public JtaDynamicDataSourceService(DynamicDataSourceConfigRepository<AtomikosDataSourceConfig> repository, DynamicDataSource defaultDataSource) {
+        super(repository, defaultDataSource);
+    }
+
+    public JtaDynamicDataSourceService(DynamicDataSourceConfigRepository<AtomikosDataSourceConfig> repository, DataSource dataSource) throws SQLException {
+        super(repository, dataSource);
+    }
+
     @Autowired(required = false)
     public void setExecutor(Executor executor) {
         this.executor = executor;
     }
 
-    public JtaDynamicDataSourceService(JtaDataSourceRepository jtaDataSourceRepository, DynamicDataSource defaultDataSource) {
-        super(defaultDataSource);
-        this.jtaDataSourceRepository = jtaDataSourceRepository;
-    }
 
-    public JtaDynamicDataSourceService(JtaDataSourceRepository jtaDataSourceRepository, DataSource dataSource) throws SQLException {
-        super(dataSource);
-        this.jtaDataSourceRepository = jtaDataSourceRepository;
-    }
 
-    @Override
-    protected int getHash(String id) {
-        AtomikosDataSourceConfig config = jtaDataSourceRepository.getConfig(id);
-        if (null == config) {
-            return 0;
-        }
-        return config.hashCode();
-    }
 
     @Override
-    protected DataSourceCache createCache(String id) {
-        AtomikosDataSourceConfig config = jtaDataSourceRepository.getConfig(id);
-        if (config == null) {
-            throw new DataSourceNotFoundException(id);
-        }
+    protected DataSourceCache createCache(AtomikosDataSourceConfig config) {
         AtomikosDataSourceBean atomikosDataSourceBean = new AtomikosDataSourceBean();
         config.putProperties(atomikosDataSourceBean);
-        atomikosDataSourceBean.setBeanName("dynamic_ds_" + id);
-        atomikosDataSourceBean.setUniqueResourceName("dynamic_ds_" + id);
+        atomikosDataSourceBean.setBeanName("dynamic_ds_" + config.getId());
+        atomikosDataSourceBean.setUniqueResourceName("dynamic_ds_" + config.getId());
         AtomicInteger successCounter = new AtomicInteger();
         CountDownLatch downLatch = new CountDownLatch(1);
         try {
-            DataSourceCache cache = new DataSourceCache(config.hashCode(), new DynamicDataSourceProxy(id, atomikosDataSourceBean), downLatch) {
+            DataSourceCache cache = new DataSourceCache(config.hashCode(), new DynamicDataSourceProxy(config.getId(), atomikosDataSourceBean), downLatch, config) {
                 @Override
                 public void closeDataSource() {
                     super.closeDataSource();
@@ -92,7 +79,7 @@ public class JtaDynamicDataSourceService extends AbstractDynamicDataSourceServic
                     successCounter.incrementAndGet();
                     downLatch.countDown();
                 } catch (Exception e) {
-                    logger.error("init datasource {} error", id, e);
+                    logger.error("init datasource {} error", config.getId(), e);
 
                     //atomikosDataSourceBean.close();
                 }

+ 0 - 29
hsweb-datasource/hsweb-datasource-jta/src/main/java/org/hswebframework/web/datasource/jta/MemoryJtaDataSourceRepository.java

@@ -1,29 +0,0 @@
-package org.hswebframework.web.datasource.jta;
-
-import org.springframework.boot.context.properties.ConfigurationProperties;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * TODO 完成注释
- *
- * @author zhouhao
- */
-@ConfigurationProperties(prefix = "hsweb.dynamic")
-public class MemoryJtaDataSourceRepository implements JtaDataSourceRepository {
-    private Map<String, AtomikosDataSourceConfig> datasource = new HashMap<>();
-
-    @Override
-    public AtomikosDataSourceConfig getConfig(String id) {
-        return datasource.get(id);
-    }
-
-    public Map<String, AtomikosDataSourceConfig> getDatasource() {
-        return datasource;
-    }
-
-    public void setDatasource(Map<String, AtomikosDataSourceConfig> datasource) {
-        this.datasource = datasource;
-    }
-}

+ 26 - 0
hsweb-datasource/hsweb-datasource-web/pom.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>hsweb-datasource</artifactId>
+        <groupId>org.hswebframework.web</groupId>
+        <version>3.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>hsweb-datasource-web</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.hswebframework.web</groupId>
+            <artifactId>hsweb-datasource-api</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.hswebframework.web</groupId>
+            <artifactId>hsweb-commons-controller</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>

+ 33 - 0
hsweb-datasource/hsweb-datasource-web/src/main/java/org/hswebframework/web/datasource/web/DatasourceController.java

@@ -0,0 +1,33 @@
+package org.hswebframework.web.datasource.web;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.hswebframework.web.authorization.Permission;
+import org.hswebframework.web.authorization.annotation.Authorize;
+import org.hswebframework.web.controller.message.ResponseMessage;
+import org.hswebframework.web.datasource.config.DynamicDataSourceConfig;
+import org.hswebframework.web.datasource.config.DynamicDataSourceConfigRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/datasource")
+@Api(tags = "开发人员工具-数据源", value = "数据源")
+@Authorize(permission = "datasource", description = "数据源管理")
+public class DatasourceController {
+
+    @Autowired
+    private DynamicDataSourceConfigRepository<? extends DynamicDataSourceConfig> repository;
+
+    @GetMapping
+    @Authorize(action = Permission.ACTION_QUERY)
+    @ApiOperation("获取全部数据源信息")
+    public ResponseMessage<List<? extends DynamicDataSourceConfig>> getAllConfig() {
+        return ResponseMessage.ok(repository.findAll());
+    }
+
+}

+ 13 - 0
hsweb-datasource/hsweb-datasource-web/src/main/java/org/hswebframework/web/datasource/web/DatasourceWebApiAutoConfiguration.java

@@ -0,0 +1,13 @@
+package org.hswebframework.web.datasource.web;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class DatasourceWebApiAutoConfiguration {
+
+    @Bean
+    public DatasourceController datasourceController() {
+        return new DatasourceController();
+    }
+}

+ 3 - 0
hsweb-datasource/hsweb-datasource-web/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,3 @@
+# Auto Configure
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+org.hswebframework.web.datasource.web.DatasourceWebApiAutoConfiguration

+ 1 - 0
hsweb-datasource/pom.xml

@@ -14,6 +14,7 @@
     <modules>
         <module>hsweb-datasource-api</module>
         <module>hsweb-datasource-jta</module>
+        <module>hsweb-datasource-web</module>
     </modules>