Browse Source

代码升级

liukx 3 years ago
parent
commit
8fc9ff7dbc
73 changed files with 2275 additions and 101 deletions
  1. 5 0
      README.md
  2. 6 0
      elab-annotation/src/main/java/com/elab/annotation/Description.java
  3. 10 2
      elab-core/src/main/java/com/elab/core/async/pruducer/TaskProducer.java
  4. 8 2
      elab-core/src/main/java/com/elab/core/componts/LoadContextTool.java
  5. 15 0
      elab-core/src/main/java/com/elab/core/exception/RealTimeQueueException.java
  6. 12 12
      elab-db/src/test/java/com.db.service/main/BasicBaseDaoProxyCase.java
  7. 6 0
      elab-db/src/test/java/com/elab/core/dao/params/NamedParameterUtils2Test.java
  8. 1 1
      elab-db/src/test/resources/applicationContext-datasource.xml
  9. 0 23
      elab-es/README.md
  10. 9 1
      elab-es/src/main/java/com/elab/es/client/configuration/ESConfiguration.java
  11. 31 6
      elab-log/src/main/java/com/elab/log/ext/CatIdMonitorRule.java
  12. 11 0
      elab-log/src/main/java/com/elab/log/utils/EventProcessUtils.java
  13. 45 5
      elab-mongodb/src/test/java/com/elab/test/mongodb/MongodbTest.java
  14. 5 0
      elab-redis/pom.xml
  15. 210 0
      elab-redis/src/main/java/com/elab/redis/CacheTemplate.java
  16. 69 0
      elab-redis/src/main/java/com/elab/redis/componts/config/ConfigCenterRedisStater.java
  17. 18 0
      elab-redis/src/main/java/com/elab/redis/componts/config/ListenerAutoConfiguration.java
  18. 23 0
      elab-redis/src/main/java/com/elab/redis/componts/config/RedisMsgListener.java
  19. 38 0
      elab-redis/src/main/java/com/elab/redis/componts/config/RedisRefreshConfigure.java
  20. 229 0
      elab-redis/src/main/java/com/elab/redis/componts/config/scope/RedisConfigRefreshCallback.java
  21. 52 0
      elab-redis/src/main/java/com/elab/redis/componts/config/scope/RedisConfigRefreshHelper.java
  22. 41 0
      elab-redis/src/main/java/com/elab/redis/componts/config/scope/RefreshConfigScope.java
  23. 29 0
      elab-redis/src/main/java/com/elab/redis/componts/config/scope/RefreshConfigScopeRegistry.java
  24. 12 0
      elab-redis/src/main/java/com/elab/redis/config/ElabRedisProperties.java
  25. 61 0
      elab-redis/src/main/java/com/elab/redis/config/props/ConfigRefreshProperties.java
  26. 6 1
      elab-redis/src/main/java/com/elab/redis/consts/CacheConstants.java
  27. 1 0
      elab-redis/src/main/java/com/elab/redis/interceptor/impl/CacheLoopProcessImpl.java
  28. 29 0
      elab-redis/src/main/java/com/elab/redis/utils/StringSerializerUtils.java
  29. 41 0
      elab-redis/src/test/java/com/elab/redis/componts/config/ConfigCenterRedisStaterTest.java
  30. 24 0
      elab-redis/src/test/java/com/elab/redis/componts/config/ConfigController.java
  31. 38 6
      elab-redis/src/test/java/com/elab/redis/redisson/RedissonAutoConfigurationTest.java
  32. 1 0
      elab-redis/src/test/java/com/elab/redis/service/impl/DemoServiceImpl.java
  33. 91 4
      elab-redis/src/test/java/com/elab/redis/spring/SpringDataTest.java
  34. 18 9
      elab-redis/src/test/java/com/elab/redis/utils/TestUtils.java
  35. 10 1
      elab-redis/src/test/resources/application.yml
  36. 5 4
      elab-spring/src/main/java/com/elab/spring/anno/MQOperation.java
  37. 16 0
      elab-spring/src/main/java/com/elab/spring/componts/conig/ConfigRefresh.java
  38. 44 0
      elab-spring/src/main/java/com/elab/spring/componts/conig/ConfigRefreshHelper.java
  39. 22 0
      elab-spring/src/main/java/com/elab/spring/componts/conig/RedisConfigRefresh.java
  40. 54 0
      elab-spring/src/main/java/com/elab/spring/componts/containers/ContainerRefresh.java
  41. 28 0
      elab-spring/src/main/java/com/elab/spring/componts/containers/ContainerRefreshRunner.java
  42. 27 0
      elab-spring/src/main/java/com/elab/spring/config/ConfigRefreshAutoConfig.java
  43. 38 0
      elab-spring/src/main/java/com/elab/spring/config/prop/SpringAutoProperties.java
  44. 0 2
      elab-spring/src/main/java/com/elab/spring/config/prop/ThreadProperties.java
  45. 7 0
      elab-spring/src/main/java/com/elab/spring/enums/ConfigRefreshEnum.java
  46. 21 21
      elab-spring/src/main/java/com/elab/spring/utils/RestTemplateUtils.java
  47. 40 0
      elab-spring/src/main/java/com/elab/spring/utils/SpringEventUtils.java
  48. 32 0
      elab-spring/src/main/java/com/elab/spring/utils/ThreadProcessUtils.java
  49. 2 1
      elab-spring/src/test/java/com/elab/spring/anno/TestAnnotation.java
  50. 60 0
      elab-spring/src/test/java/com/elab/spring/design/group/AbstractInvocationProcess.java
  51. 14 0
      elab-spring/src/test/java/com/elab/spring/design/group/GroupAdaptor.java
  52. 53 0
      elab-spring/src/test/java/com/elab/spring/design/group/GroupInvocationManager.java
  53. 24 0
      elab-spring/src/test/java/com/elab/spring/design/group/GroupResult.java
  54. 25 0
      elab-spring/src/test/java/com/elab/spring/design/group/GroupRouteInvocation.java
  55. 9 0
      elab-spring/src/test/java/com/elab/spring/design/group/SubscribeInvocation.java
  56. 7 0
      elab-spring/src/test/java/com/elab/spring/design/group/test/GroupConstant.java
  57. 27 0
      elab-spring/src/test/java/com/elab/spring/design/group/test/Main.java
  58. 22 0
      elab-spring/src/test/java/com/elab/spring/design/group/test/TestGroupProcess.java
  59. 25 0
      elab-spring/src/test/java/com/elab/spring/design/group/test/group/AGroup.java
  60. 25 0
      elab-spring/src/test/java/com/elab/spring/design/group/test/group/BGroup.java
  61. 25 0
      elab-spring/src/test/java/com/elab/spring/design/group/test/group/CGroup.java
  62. 17 0
      elab-spring/src/test/java/com/elab/spring/utils/Source2ApplicationListener.java
  63. 14 0
      elab-spring/src/test/java/com/elab/spring/utils/Source3ApplicationListener.java
  64. 19 0
      elab-spring/src/test/java/com/elab/spring/utils/SourceApplicationListener.java
  65. 119 0
      elab-spring/src/test/java/com/elab/spring/utils/SpringCase.java
  66. 30 0
      elab-spring/src/test/java/com/elab/spring/utils/SpringEventUtilsTest.java
  67. 16 0
      elab-spring/src/test/java/com/elab/spring/utils/event/EventA.java
  68. 15 0
      elab-spring/src/test/java/com/elab/spring/utils/event/EventB.java
  69. 14 0
      elab-spring/src/test/java/com/elab/spring/utils/event/SourceModel.java
  70. 33 0
      elab-test/pom.xml
  71. 58 0
      elab-test/src/test/java/com/elab/my/github/FileCase.java
  72. 82 0
      elab-test/src/test/java/com/elab/my/maven/TestServiceTest.java
  73. 1 0
      pom.xml

+ 5 - 0
README.md

@@ -10,6 +10,11 @@
 - elab-spring : 对Spring的一些拓展封装
 - [elab-kafka](./elab-kafka/README.md) : 基于Kafka使用的封装
 
+
+### 2.0.5.3-SNAPSHOT 改动记录
+1. 新增redis的bitmap、BloomFilter、HyperLogLog等操作;
+2. 
+
 ### 2.0.5.2-SNAPSHOT 改动记录
 1. 新增线程并行工具类,串联日志。
 2. 调整redis序列化: 兼容fastjson\jackson

+ 6 - 0
elab-annotation/src/main/java/com/elab/annotation/Description.java

@@ -11,9 +11,15 @@ import java.lang.annotation.*;
 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
 @Documented
+@Author
 @Inherited
 public @interface Description {
 
+    /**
+     * 功能描述
+     *
+     * @return
+     */
     String remark() default "";
 
 }

+ 10 - 2
elab-core/src/main/java/com/elab/core/async/pruducer/TaskProducer.java

@@ -7,6 +7,7 @@ import com.elab.core.async.TaskStoreProcess;
 import com.elab.core.async.model.TaskStoreData;
 import com.elab.core.async.store.TaskExecutorDecoration;
 import com.elab.core.async.store.TaskExecutorQueue;
+import com.elab.core.exception.RealTimeQueueException;
 import com.google.common.collect.Lists;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -55,13 +56,20 @@ public class TaskProducer implements ITaskProducer {
             handAdd(taskStoreData);
         }
         TaskExecutorDecoration taskExecutorDecoration = new TaskExecutorDecoration(taskStoreData, taskExecutor);
-        boolean isConsumer = this.taskExecutorQueue.getRealTimeQueue().offer(taskExecutorDecoration);
+
+        Queue<TaskExecutorDecoration> realTimeQueue = this.taskExecutorQueue.getRealTimeQueue();
+
+        boolean isConsumer = realTimeQueue.offer(taskExecutorDecoration);
         if (!isConsumer) {
             long currentTimeMillis = System.currentTimeMillis();
             long current = currentTimeMillis - startTime;
             if (current > maxWarnTime) {
                 startTime = currentTimeMillis;
-                logger.error("队列满了!");
+                RealTimeQueueException realTimeQueueException =
+                    new RealTimeQueueException("异步队列消费失败: 当前大小:" + realTimeQueue.size());
+                logger.error("队列满了!", realTimeQueueException);
+            } else {
+                logger.warn("本地队列满了");
             }
         }
         return isConsumer;

+ 8 - 2
elab-core/src/main/java/com/elab/core/componts/LoadContextTool.java

@@ -1,6 +1,10 @@
 package com.elab.core.componts;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * @Module 加载工具类
@@ -9,7 +13,7 @@ import java.util.concurrent.CountDownLatch;
  * @Date 2020/12/24 14:30
  */
 public class LoadContextTool {
-
+    private Logger logger = LoggerFactory.getLogger(getClass());
     private volatile boolean isLoadFinish = false;
 
     private CountDownLatch countDownLatch;
@@ -21,7 +25,9 @@ public class LoadContextTool {
     public void awaitFinish() {
         if (!isLoadFinish) {
             try {
-                countDownLatch.await();
+                logger.warn("等待应用程序唤醒...");
+                countDownLatch.await(30, TimeUnit.SECONDS);
+                logger.debug("唤醒成功...");
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }

+ 15 - 0
elab-core/src/main/java/com/elab/core/exception/RealTimeQueueException.java

@@ -0,0 +1,15 @@
+package com.elab.core.exception;
+
+/**
+ * 实时队列异常
+ *
+ * @author liukaixiong
+ * @Email liukx@elab-plus.com
+ * @date 2021/8/23 - 14:24
+ */
+public class RealTimeQueueException extends RuntimeException {
+
+    public RealTimeQueueException(String message) {
+        super(message);
+    }
+}

+ 12 - 12
elab-db/src/test/java/com.db.service/main/BasicBaseDaoProxyCase.java

@@ -56,18 +56,18 @@ public class BasicBaseDaoProxyCase {
 
     @Test
     public void testUpdateCase() throws Exception {
-//        TTest test = new TTest();
-//        test.setId(1);
-//        test.setStatus("1");
-//        test.setLoveName("aaaaa");
-//        test.setUsername("某某某xx33333333");
-//        int insert = testDao.updateById(test);
-//        System.out.println(insert);
-
-        String json = "{\"brandId\":1,\"dispatchOrderStatus\":1,\"mobileTime\":1614061782345,\"source\":1,\"updated\":1614061782345,\"userId\":34958}";
-        LinkedHashMap map = JSON.parseObject(json, LinkedHashMap.class);
-        map.put("houseId","100");
-        int i = testDao.updateUserInfo(map);
+        TTest test = new TTest();
+        test.setId(1);
+        test.setStatus("1");
+        test.setLoveName("aaaaa");
+        test.setUsername("某某某xx33333333");
+        int insert = testDao.updateById(test);
+        System.out.println(insert);
+
+//        String json = "{\"brandId\":1,\"dispatchOrderStatus\":1,\"mobileTime\":1614061782345,\"source\":1,\"updated\":1614061782345,\"userId\":34958}";
+//        LinkedHashMap map = JSON.parseObject(json, LinkedHashMap.class);
+//        map.put("houseId","100");
+//        int i = testDao.updateUserInfo(map);
 //        Assert.assertTrue(insert > 0);
     }
 

+ 6 - 0
elab-db/src/test/java/com/elab/core/dao/params/NamedParameterUtils2Test.java

@@ -18,6 +18,12 @@ public class NamedParameterUtils2Test {
 
     @Test
     public void parseSqlStatement() {
+        int i = 0;
+        System.out.println("---"+test(i));
+    }
+
+    private boolean test(Object num){
+        return num instanceof Number;
     }
 
     @Test

+ 1 - 1
elab-db/src/test/resources/applicationContext-datasource.xml

@@ -15,7 +15,7 @@
 
     <bean id="mysqlDataSource" class="com.alibaba.druid.pool.DruidDataSource">
         <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
-        <property name="url" value="jdbc:mysql://192.168.0.11:3306/elab_db?characterEncoding=utf-8"/>
+        <property name="url" value="jdbc:mysql://192.168.0.11:3306/elab_db?useSSL=false"/>
         <property name="username" value="root"/>
         <property name="password" value="Elab@123"/>
         <property name="initialSize" value="50"/>

+ 0 - 23
elab-es/README.md

@@ -190,20 +190,6 @@ System.out.println(exists);
 
 原生查询
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
 ####  原生查询
 
 searchRequest是官方原生查询输入,此方法在工具无法满足需求时使用
@@ -229,16 +215,7 @@ for (SearchHit hit : searchHits) {
 
 [参考文档](https://gitee.com/zxporz/ESClientRHL)
 
-
-
-
-
 #### 高亮查询
-
-
-
-
-
 ## 关于集成Spring-jdbc记录:
 
 1. 尝试过使用 [ElasticSearch-sql](https://github.com/NLPchina/elasticsearch-sql)去做SQL转换,兼容Druid数据源等等。但是由于ES以后的版本都只会往**RestHighLevelClient**靠,但是该框架又是基于老的`Client`接口去做的,8.0版本之后会被移除,所以还得看看其他方式

+ 9 - 1
elab-es/src/main/java/com/elab/es/client/configuration/ESConfiguration.java

@@ -5,6 +5,7 @@ import org.apache.http.HttpHost;
 import org.apache.http.auth.AuthScope;
 import org.apache.http.auth.UsernamePasswordCredentials;
 import org.apache.http.client.CredentialsProvider;
+import org.apache.http.conn.ConnectionKeepAliveStrategy;
 import org.apache.http.impl.client.BasicCredentialsProvider;
 import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
 import org.elasticsearch.client.RestClient;
@@ -63,16 +64,23 @@ public class ESConfiguration {
                     .setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
                         @Override
                         public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
+                            httpClientBuilder.setKeepAliveStrategy(getConnectionKeepAliveStrategy());
                             return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                         }
                     });
             // RestHighLevelClient实例通过REST low-level client builder进行构造。
-            return new RestHighLevelClient(builder);
+            RestHighLevelClient restHighLevelClient = new RestHighLevelClient(builder);
+            return restHighLevelClient;
         } else if (properties.getUris().size() > 1) {
             throw new Exception("bucket type is not support");
         }
         throw new Exception("RestHighLevelClient 需要指定URL。yaml配置中elab.elasticsearch.uris ..");
     }
+    private ConnectionKeepAliveStrategy getConnectionKeepAliveStrategy() {
+        return (response, context) -> {
+            return 2 * 60 * 1000;
+        };
+    }
 
 
 }

+ 31 - 6
elab-log/src/main/java/com/elab/log/ext/CatIdMonitorRule.java

@@ -3,10 +3,15 @@ package com.elab.log.ext;
 import com.alibaba.fastjson.JSON;
 import com.dianping.cat.Cat;
 import com.elab.log.model.CatCrossIdModel;
+import com.elab.log.utils.EventProcessUtils;
 import com.jay.monitor.data.client.ext.MonitorRuleCallback;
 import com.jay.monitor.data.core.model.serializable.base.BaseDTO;
 import com.jay.monitor.data.core.model.serializable.base.TranceLogData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 import java.util.function.BiConsumer;
 
@@ -16,21 +21,41 @@ import java.util.function.BiConsumer;
  * @author liukaixong
  */
 public class CatIdMonitorRule implements MonitorRuleCallback {
+    private Logger logger = LoggerFactory.getLogger(getClass());
 
     @Override
     public void registerRule(Map<String, BiConsumer<String, ? super BaseDTO>> ruleMap) {
         ruleMap.put(MonitorRuleCallback.allMatchRuleType, (value, mqData) -> {
+
             if (mqData instanceof TranceLogData) {
-                CatCrossIdModel catCrossIdModel = JSON.parseObject(value, CatCrossIdModel.class);
-                TranceLogData tranceLogData = (TranceLogData) mqData;
-                if (catCrossIdModel.get_catRootMessageId() != null) {
-                    tranceLogData.setRootLogId(catCrossIdModel.get_catRootMessageId());
-                    tranceLogData.setParentLogId(catCrossIdModel.get_catParentMessageId());
-                }
+                TranceLogData tranceLogData = (TranceLogData)mqData;
+                invokeTraceId(value, tranceLogData);
                 // 这里不用get_catChildMessageId的原因是因为生产者是不确定下游的消费者是谁的。
                 // 这里还是用当前线程产生的LogId。这个id非常重要。
                 tranceLogData.setLogId(Cat.getCurrentMessageId());
             }
+
         });
     }
+
+    private void invokeTraceId(String value, TranceLogData tranceLogData) {
+        try {
+            CatCrossIdModel catCrossIdModel = JSON.parseObject(value, CatCrossIdModel.class);
+            if (catCrossIdModel.get_catRootMessageId() != null) {
+                tranceLogData.setRootLogId(catCrossIdModel.get_catRootMessageId());
+                tranceLogData.setParentLogId(catCrossIdModel.get_catParentMessageId());
+            }
+        } catch (Exception e) {
+            logger.warn("消息处理异常" + e.getMessage() + "\t 异常的值: " + value);
+            EventProcessUtils.warnMsg("CatIdMonitorRule_warn", e.getMessage() + "\t" + value);
+        }
+    }
+
+    public static void main(String[] args) {
+        List<CatCrossIdModel> list = new ArrayList<>();
+        list.add(new CatCrossIdModel());
+        String value = JSON.toJSONString(list);
+        CatCrossIdModel catCrossIdModel = JSON.parseObject(value, CatCrossIdModel.class);
+        System.out.println(catCrossIdModel);
+    }
 }

+ 11 - 0
elab-log/src/main/java/com/elab/log/utils/EventProcessUtils.java

@@ -11,6 +11,8 @@ import com.elab.core.utils.ObjectUtils;
  */
 public class EventProcessUtils {
 
+    private static final String WARN_KEY = "WARN_LOG";
+
     /**
      * 写入Event事件
      *
@@ -80,5 +82,14 @@ public class EventProcessUtils {
         }
     }
 
+    /**
+     * 埋入比较关注的点
+     *
+     * @param key   唯一的key
+     * @param value 值
+     */
+    public static void warnMsg(String key, String value) {
+        write(WARN_KEY, key, value);
+    }
 
 }

+ 45 - 5
elab-mongodb/src/test/java/com/elab/test/mongodb/MongodbTest.java

@@ -4,7 +4,7 @@ import com.alibaba.fastjson.JSONObject;
 import com.elab.test.mongodb.entity.User;
 import com.elab.test.mongodb.ext.JsonObjectDb;
 import com.elab.test.mongodb.ext.UserMongoDB;
-import com.mongodb.MongoClient;
+import com.mongodb.*;
 import org.junit.Test;
 import org.springframework.data.mongodb.core.MongoTemplate;
 import org.springframework.data.mongodb.core.SimpleMongoDbFactory;
@@ -12,10 +12,7 @@ import org.springframework.data.mongodb.core.query.Criteria;
 import org.springframework.data.mongodb.core.query.Query;
 
 import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * MongoDB测试类
@@ -120,4 +117,47 @@ public class MongodbTest {
         System.out.println(copy_of_rental_house.size());
     }
 
+    @Test
+    public void findCollectField() throws Exception {
+
+        String username = "elab";
+        String password = "elab8888";
+        String mongodbName = "mofang";
+        List<ServerAddress> serverAddressList = new ArrayList<>();
+        serverAddressList.add(new ServerAddress("106.15.201.221", 27017));
+
+        MongoCredential userCredentials =
+            MongoCredential.createCredential(username, mongodbName, password.toCharArray());
+        List<MongoCredential> userCredentialsList = new ArrayList<>();
+        userCredentialsList.add(userCredentials);
+        MongoClient mongoClient = new MongoClient(serverAddressList, userCredentialsList);
+
+        SimpleMongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(mongoClient, mongodbName);
+        MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory);
+
+        Set<String> collectionNames = mongoTemplate.getCollectionNames();
+        BasicDBObject doc = new BasicDBObject();
+        DBObject dbObject = new BasicDBObject("$natural", -1);
+
+        String fieldType = "string";
+        for (String name : collectionNames) {
+            System.out.println("----------------------------" + name + "---------------------------");
+            try {
+                DBObject collectionObject = mongoTemplate.getCollection(name).find(doc).sort(dbObject).limit(1).next();
+                Set<String> keySet = collectionObject.keySet();
+                for (String key : keySet) {
+                    Object o = collectionObject.get(key);
+                    String defaultValue = fieldType;
+                    if (o instanceof BasicDBList) {
+                        defaultValue = "array";
+                    }
+                    System.out.println(key + "\t" + defaultValue);
+                }
+            } catch (Exception e) {
+                System.out.println(e.getMessage());
+            }
+        }
+
+    }
+
 }

+ 5 - 0
elab-redis/pom.xml

@@ -17,6 +17,11 @@
             <artifactId>redisson-spring-boot-starter</artifactId>
             <version>3.8.2</version>
         </dependency>
+<!--        <dependency>-->
+<!--            <groupId>com.posiedon.wh</groupId>-->
+<!--            <artifactId>config-center-redis-spring-boot-autoconfigure</artifactId>-->
+<!--            <version>0.1.0</version>-->
+<!--        </dependency>-->
         <!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
         <dependency>
             <groupId>javax.validation</groupId>

+ 210 - 0
elab-redis/src/main/java/com/elab/redis/CacheTemplate.java

@@ -1,10 +1,18 @@
 package com.elab.redis;
 
+import com.elab.redis.utils.StringSerializerUtils;
+import org.redisson.api.RBloomFilter;
+import org.redisson.api.RHyperLogLog;
 import org.redisson.api.RedissonClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.dao.DataAccessException;
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.connection.RedisStringCommands;
 import org.springframework.data.redis.core.*;
 
+import java.util.*;
+
 /**
  * 缓存直接操作类
  *
@@ -57,5 +65,207 @@ public class CacheTemplate {
         return redisTemplate.opsForZSet();
     }
 
+    public boolean bitAdd(String key, Integer value) {
+        return string().setBit(key, value, true);
+    }
+
+    /**
+     * 在特定的位置设置值
+     *
+     * @param key
+     * @param offset
+     * @return
+     */
+    public boolean bitSet(String key, long offset) {
+        return bitSet(key, offset, true);
+    }
+
+    /**
+     * 在特定的位置设置值
+     *
+     * @param key    key
+     * @param offset 偏移位置
+     * @param value  设置是否存在
+     * @return
+     */
+    public boolean bitSet(String key, long offset, boolean value) {
+        return string().setBit(key, offset, value);
+    }
+
+    /**
+     * 获取特定偏移量的位置是否存在值
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public boolean bitGet(String key, long value) {
+        return string().getBit(key, value);
+    }
+
+    /**
+     * 统计特定key的bitMap的And模式
+     * <p>
+     * 偏亮位置中的值都为1则为true,否则false.
+     *
+     * @param newKey
+     * @param offset
+     * @param keys
+     * @return
+     */
+    public boolean bitAndGet(String newKey, long offset, String... keys) {
+        // 如果不存在的key则创建
+        bitBiTop(RedisStringCommands.BitOperation.AND, newKey, false, keys);
+        return bitGet(newKey, offset);
+    }
+
+    /**
+     * 统计某个key中的bit为1的总数
+     *
+     * @param key
+     * @return
+     */
+    public Long bitCount(String key) {
+
+        byte[] keyByte = StringSerializerUtils.serialize(key);
+
+        return redisTemplate.execute(new RedisCallback<Long>() {
+            @Override
+            public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {
+                return redisConnection.bitCount(keyByte);
+            }
+        });
+    }
+
+    public boolean bitBiTop(RedisStringCommands.BitOperation bitOperation, String newKey, List<String> keyList) {
+        String[] keys = new String[keyList.size()];
+        keyList.toArray(keys);
+        return bitBiTop(bitOperation, newKey, true, keys);
+    }
+
+    /**
+     * 创建一个新的key来与Keys其他key做运算
+     *
+     * @param bitOperation -> RedisStringCommands.BitOperation.AND || RedisStringCommands.BitOperation.XOR
+     * @param isReplace    如果key已经存在是否替换
+     * @param newKey       新的key的名称
+     * @param keys         运算的keys
+     * @return
+     */
+    public boolean bitBiTop(RedisStringCommands.BitOperation bitOperation, String newKey, boolean isReplace,
+        String... keys) {
+
+        if (keys == null || keys.length < 2) {
+            return false;
+        }
+
+        if (!isReplace && redisTemplate.hasKey(newKey)) {
+            return true;
+        }
+
+        byte[] newKeyBytes = StringSerializerUtils.serialize(newKey);
+        byte[][] keysBytes = StringSerializerUtils.serializeMulti(keys);
+
+        return redisTemplate.execute(new RedisCallback<Long>() {
+            @Override
+            public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {
+                return redisConnection.bitOp(bitOperation, newKeyBytes, keysBytes);
+            }
+        }) > 0 ? true : false;
+    }
+
+    /**
+     * 批量key获取对应的值
+     *
+     * @param keys
+     * @param offset
+     * @return
+     */
+    public Map<String, Boolean> bitRangeGet(List<String> keys, long offset) {
+        Map<String, Boolean> resultMap = new LinkedHashMap<>();
+        for (String key : keys) {
+            boolean result = bitGet(key, offset);
+            resultMap.put(key, result);
+        }
+        return resultMap;
+    }
+
+    /**
+     * 用于大数据计数,适用于对数据准确度不高的大数据计数场景
+     * 底层是Set结构: 添加值
+     *
+     * @param key
+     * @param value
+     * @return
+     */
+    public boolean hyperLogAdd(String key, Object value) {
+        RHyperLogLog<Object> hyperLogLog = redissonClient.getHyperLogLog(key);
+        return hyperLogLog.add(value);
+    }
+
+    /**
+     * 获取总数
+     *
+     * @param key
+     * @return
+     */
+    public long hyperLogCount(String key) {
+        RHyperLogLog<Object> hyperLogLog = redissonClient.getHyperLogLog(key);
+        return hyperLogLog.count();
+    }
+
+    /**
+     * 获取HyperLogLog对象
+     * <p>
+     * >> 用于大数据计数,适用于对数据准确度不高的大数据计数场景
+     *
+     * @param key
+     * @return
+     */
+    public RHyperLogLog<Object> hyperLogObject(String key) {
+        return redissonClient.getHyperLogLog(key);
+    }
+
+    /**
+     * 创建布隆过滤器
+     *
+     * @param key
+     * @param expectedInsertions
+     * @param falseProbability
+     * @return
+     */
+    public boolean bloomFilterCreate(String key, Integer expectedInsertions, double falseProbability) {
+        RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(key);
+        return bloomFilter.tryInit(expectedInsertions, falseProbability);
+    }
+
+    /**
+     * 布隆过滤器 添加值
+     *
+     * @param key
+     * @param obj
+     * @return
+     */
+    public boolean bloomFilterAdd(String key, Object obj) {
+        RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(key);
+        return bloomFilter.add(obj);
+    }
+
+    /**
+     * 布隆过滤器 包含的值
+     *
+     * @param key
+     * @param object
+     * @return
+     */
+    public boolean bloomFilterContains(String key, Object object) {
+        RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(key);
+        return bloomFilter.contains(object);
+    }
+
+    public RBloomFilter<Object> bloomFilter(String key) {
+        return redissonClient.getBloomFilter(key);
+    }
+
 }
 

+ 69 - 0
elab-redis/src/main/java/com/elab/redis/componts/config/ConfigCenterRedisStater.java

@@ -0,0 +1,69 @@
+package com.elab.redis.componts.config;
+
+import com.elab.redis.componts.config.scope.RedisConfigRefreshCallback;
+import com.elab.redis.config.props.ConfigRefreshProperties;
+import com.elab.redis.config.ElabRedisProperties;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.env.Environment;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.listener.PatternTopic;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+
+import javax.annotation.PostConstruct;
+import java.util.ArrayList;
+import java.util.List;
+
+public class ConfigCenterRedisStater {
+    private Logger logger = LoggerFactory.getLogger(getClass());
+    @Autowired
+    private RedisConfigRefreshCallback refreshConfig;
+
+    @Autowired
+    private Environment environment;
+
+    @Autowired
+    private ElabRedisProperties redisProperties;
+
+    @Autowired
+    private RedisConnectionFactory redisConnectionFactory;
+
+    @Autowired
+    private RedisMessageListenerContainer messageListenerContainer;
+
+    @PostConstruct
+    public void init() {
+        messageListenerContainer.setConnectionFactory(redisConnectionFactory);
+        messageListenerContainer.addMessageListener(new RedisMsgListener(refreshConfig), topicList());
+    }
+
+    List<PatternTopic> topicList() {
+
+        ConfigRefreshProperties configRefresh = redisProperties.getConfigRefresh();
+
+        String pushKey = null;
+
+        List<PatternTopic> topics = new ArrayList<>();
+
+        if (configRefresh != null && StringUtils.isNotEmpty(configRefresh.getConfigPushCacheKey())) {
+            pushKey = configRefresh.getConfigPushCacheKey();
+        } else {
+            String appName = environment.getProperty("spring.application.name", "unknown_application");
+            String profile = environment.getProperty("spring.profiles.active", "");
+            pushKey = appName + "-" + profile;
+        }
+
+        topics.add(new PatternTopic(pushKey + "*"));
+        topics.add(new PatternTopic(pushKey.toLowerCase() + "*"));
+
+        topics.forEach((topic) -> {
+            String topic1 = topic.getTopic();
+            logger.info("redis 关注的topic : " + topic1);
+        });
+
+        return topics;
+    }
+
+}

+ 18 - 0
elab-redis/src/main/java/com/elab/redis/componts/config/ListenerAutoConfiguration.java

@@ -0,0 +1,18 @@
+//package com.elab.redis.componts.config;
+//
+//import com.elab.redis.consts.CacheConstants;
+//import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+//import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+//import org.springframework.context.annotation.Bean;
+//import org.springframework.context.annotation.Configuration;
+//
+//@Configuration
+//@ConditionalOnBean(name = {CacheConstants.REFRESH_CONFIG_SCOPE_NAME})
+//@AutoConfigureAfter(RedisRefreshConfigure.class)
+//public class ListenerAutoConfiguration {
+//
+//    @Bean
+//    ConfigCenterRedisStater configCenterRedisStater() {
+//        return new ConfigCenterRedisStater();
+//    }
+//}

+ 23 - 0
elab-redis/src/main/java/com/elab/redis/componts/config/RedisMsgListener.java

@@ -0,0 +1,23 @@
+package com.elab.redis.componts.config;
+
+import com.elab.redis.componts.config.scope.RedisConfigRefreshCallback;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.redis.connection.Message;
+import org.springframework.data.redis.connection.MessageListener;
+
+public class RedisMsgListener implements MessageListener {
+
+    private Logger logger = LoggerFactory.getLogger(getClass());
+    private final RedisConfigRefreshCallback refreshConfig;
+
+    public RedisMsgListener(RedisConfigRefreshCallback refreshConfig) {
+        this.refreshConfig = refreshConfig;
+    }
+
+    @Override
+    public void onMessage(Message message, byte[] pattern) {
+        logger.debug("监听缓存发生变化:" + new String(message.getBody()) + "\t topic : " + new String(pattern));
+        refreshConfig.refresh();
+    }
+}

+ 38 - 0
elab-redis/src/main/java/com/elab/redis/componts/config/RedisRefreshConfigure.java

@@ -0,0 +1,38 @@
+package com.elab.redis.componts.config;
+
+import com.elab.redis.componts.config.scope.RedisConfigRefreshCallback;
+import com.elab.redis.componts.config.scope.RefreshConfigScopeRegistry;
+import com.elab.redis.consts.CacheConstants;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.listener.RedisMessageListenerContainer;
+
+@Configuration
+@ConditionalOnProperty(prefix = "spring.redis.elab.config-refresh", value = "enable", havingValue = "true")
+public class RedisRefreshConfigure {
+
+    @Bean
+    public RefreshConfigScopeRegistry refreshConfigScopeRegistry() {
+        return new RefreshConfigScopeRegistry();
+    }
+
+    @Bean(CacheConstants.REFRESH_CONFIG_SCOPE_NAME)
+    @ConditionalOnBean(RefreshConfigScopeRegistry.class)
+    public RedisConfigRefreshCallback refreshConfig() {
+        return new RedisConfigRefreshCallback();
+    }
+
+    @Bean
+    @ConditionalOnBean({RedisConfigRefreshCallback.class})
+    public RedisMessageListenerContainer redisMessageListenerContainer() {
+        return new RedisMessageListenerContainer();
+    }
+
+    @Bean
+    @ConditionalOnBean(name = {CacheConstants.REFRESH_CONFIG_SCOPE_NAME})
+    public ConfigCenterRedisStater configCenterRedisStater() {
+        return new ConfigCenterRedisStater();
+    }
+}

+ 229 - 0
elab-redis/src/main/java/com/elab/redis/componts/config/scope/RedisConfigRefreshCallback.java

@@ -0,0 +1,229 @@
+package com.elab.redis.componts.config.scope;
+
+import com.elab.redis.config.props.ConfigRefreshProperties;
+import com.elab.redis.config.ElabRedisProperties;
+import com.elab.redis.consts.CacheConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.boot.env.OriginTrackedMapPropertySource;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.env.Environment;
+import org.springframework.core.env.MutablePropertySources;
+import org.springframework.core.env.PropertySource;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import javax.annotation.PostConstruct;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 核心类:
+ * <p>
+ * 1.从redis加载配置
+ * 2.更新应用配置数据
+ *
+ * <p>
+ * Enviroment propertySource 更新
+ * '@Value' 注解配置数据更新,@Value需要在类上加@Scope("RefreshConfig")
+ * </p>
+ *
+ * @Author: posiedon.wh
+ */
+public class RedisConfigRefreshCallback implements ApplicationContextAware {
+
+    private Logger logger = LoggerFactory.getLogger(RedisConfigRefreshCallback.class);
+
+    @Autowired
+    private RedisTemplate<String, String> redisTemplate;
+
+    @Autowired
+    Environment environment;
+
+    private Map configMap = new ConcurrentHashMap();
+
+    private static String redisSourceName = "redisSource";
+
+    private static String scopeName = CacheConstants.REFRESH_CONFIG_SCOPE_NAME;
+
+    private String redisConfigKey = "";
+
+    @Autowired
+    private ElabRedisProperties redisProperties;
+
+    private static ConfigurableApplicationContext applicationContext;
+
+    private BeanDefinitionRegistry beanDefinitionRegistry;
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        RedisConfigRefreshCallback.applicationContext = (ConfigurableApplicationContext)applicationContext;
+    }
+
+    public String redisRefreshKey() {
+        ConfigRefreshProperties configRefresh = redisProperties.getConfigRefresh();
+        if (configRefresh != null) {
+            return configRefresh.getConfigRefreshCacheKey();
+        }
+        return null;
+    }
+
+    @PostConstruct
+    public void init() {
+        if (!enableConfigRefresh()) {
+            return;
+        }
+
+        RefreshConfigScopeRegistry refreshConfigScopeRegister =
+            (RefreshConfigScopeRegistry)applicationContext.getBean(CacheConstants.REFRESH_CONFIG_SCOPE_REGISTRY);
+        beanDefinitionRegistry = refreshConfigScopeRegister.getBeanDefinitionRegistry();
+
+        redisConfigKey = redisRefreshKey();
+
+        refreshEnv();
+
+        syncInitConfig();
+    }
+
+    /**
+     * 加载应用中默认的配置
+     */
+    private void syncInitConfig() {
+        ConfigRefreshProperties configRefresh = redisProperties.getConfigRefresh();
+        if (configRefresh == null || configRefresh.getDefaultConfig() == null
+            || configRefresh.getDefaultConfig().size() == 0) {
+            return;
+        }
+
+        Map<String, String> defaultConfig = configRefresh.getDefaultConfig();
+
+        defaultConfig.forEach((K, V) -> {
+            redisTemplate.opsForHash().putIfAbsent(redisConfigKey, K, V);
+        });
+
+    }
+
+    //开始更新配置指令
+    public void refresh() {
+        logger.info("刷新配置...");
+        this.refreshEnv();
+    }
+
+    public boolean enableConfigRefresh() {
+        ConfigRefreshProperties configRefresh = redisProperties.getConfigRefresh();
+        if (configRefresh != null) {
+            return configRefresh.isEnable();
+        }
+        return false;
+    }
+
+    //更新environment 属性值
+    private void refreshEnv() {
+        if (!enableConfigRefresh()) {
+            return;
+        }
+
+        logger.info("start to refresh config from redis.");
+
+        //创建sourceProperty
+        if (!checkExistRedisSpringProperty()) {
+            createRedisSpringProperty();
+        }
+        try {
+            configMap = redisTemplate.opsForHash().entries(redisConfigKey);
+            logger.debug("加载可刷新缓存中:" + redisConfigKey + "的key的数据,数据长度大小:" + configMap.size());
+            // 更新单例配置工厂中的值
+            RedisConfigRefreshHelper.getInstance().refresh(configMap);
+        } catch (Exception e) {
+            logger.error("load config from redis fail,reason:" + e.getMessage());
+        }
+
+        if (configMap.isEmpty()) {
+            return;
+        }
+
+        MutablePropertySources propertySources = applicationContext.getEnvironment().getPropertySources();
+
+        PropertySource<?> source = propertySources.get(redisSourceName);
+
+        // 更新容器中配置对象的值
+        ConcurrentHashMap concurrentHashMap = (ConcurrentHashMap)source.getSource();
+
+        concurrentHashMap.clear();
+
+        Set<Object> keys = configMap.keySet();
+        for (Object key : keys) {
+            concurrentHashMap.put(key, configMap.get(key));
+        }
+
+        // 刷新相关的bean
+        refreshBean();
+
+    }
+
+    //创建redis-propertySources 环境资源
+    private void createRedisSpringProperty() {
+        MutablePropertySources propertySources = applicationContext.getEnvironment().getPropertySources();
+        OriginTrackedMapPropertySource redisSource = new OriginTrackedMapPropertySource(redisSourceName, configMap);
+        propertySources.addFirst(redisSource);
+    }
+
+    /**
+     * 检查是否已经将redis的初始化数据加载到容器中
+     *
+     * @return
+     */
+    private boolean checkExistRedisSpringProperty() {
+        MutablePropertySources propertySources = applicationContext.getEnvironment().getPropertySources();
+        if (propertySources.contains(redisSourceName)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 从容器中查找bean中scope属性为
+     */
+    private void refreshBean() {
+        String[] definitionNames = applicationContext.getBeanDefinitionNames();
+        for (String name : definitionNames) {
+            BeanDefinition beanDefinition = beanDefinitionRegistry.getBeanDefinition(name);
+            if (scopeName.equals(beanDefinition.getScope())) {
+                //销毁bean 思考,如果这时候删除了bean,有没有问题?
+                applicationContext.getBeanFactory().destroyScopedBean(name);
+                //重新实例化
+                applicationContext.getBean(name);
+            }
+        }
+    }
+
+    //获得redis 中配置key
+    //    private String getRedisPushKey() {
+    //
+    //        // 默认按照 应用名称+环境标识 作为推送key
+    //        String appName = environment.getProperty("spring.application.name");
+    //        String profile = environment.getProperty("spring.profiles.active");
+    //        String key = null;
+    //        if (appName == null || "".equals(appName)) {
+    //            key = "application";
+    //            if (profile != null && !"".equals(profile)) {
+    //                key += profile;
+    //            }
+    //        } else {
+    //            key = appName.toLowerCase();
+    //            if (profile != null && !"".equals(profile)) {
+    //                key += profile.toLowerCase();
+    //            }
+    //        }
+    //
+    //        return key;
+    //    }
+
+}

+ 52 - 0
elab-redis/src/main/java/com/elab/redis/componts/config/scope/RedisConfigRefreshHelper.java

@@ -0,0 +1,52 @@
+package com.elab.redis.componts.config.scope;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.parser.Feature;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 缓存配置工厂
+ * <p>
+ * 使用前提 : 加载 {@link RedisConfigRefreshCallback#refresh()} 数据初始化源头
+ *
+ * @author liukaixiong
+ * @Email liukx@elab-plus.com
+ */
+public class RedisConfigRefreshHelper {
+
+    public static Map<String, String> configCache = new ConcurrentHashMap<>();
+
+    private static RedisConfigRefreshHelper INSTANCE = new RedisConfigRefreshHelper();
+
+    public static RedisConfigRefreshHelper getInstance() {
+        return INSTANCE;
+    }
+
+    public <T> T get(String key, Class<T> clazz) {
+        String body = configCache.get(key);
+
+        if (StringUtils.isNotEmpty(body)) {
+            return JSON.parseObject(body, clazz, Feature.AllowUnQuotedFieldNames, Feature.SupportAutoType);
+        }
+
+        return null;
+    }
+
+    public String get(String key) {
+        return configCache.get(key);
+    }
+
+    /**
+     * 刷新缓存配置
+     * {@link com.elab.redis.componts.config.scope.RedisConfigRefreshCallback#refresh()}
+     *
+     * @param configMap
+     */
+    protected synchronized void refresh(Map<String, String> configMap) {
+        this.configCache = configMap;
+    }
+
+}

+ 41 - 0
elab-redis/src/main/java/com/elab/redis/componts/config/scope/RefreshConfigScope.java

@@ -0,0 +1,41 @@
+package com.elab.redis.componts.config.scope;
+
+import org.springframework.beans.factory.ObjectFactory;
+import org.springframework.beans.factory.config.Scope;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+public class RefreshConfigScope implements Scope {
+
+    private final ConcurrentHashMap<String,Object> caches=new ConcurrentHashMap<>();
+
+    @Override
+    public Object get(String s, ObjectFactory<?> objectFactory) {
+        if(caches.containsKey(s)){
+            return caches.get(s);
+        }
+        Object object = objectFactory.getObject();
+        caches.put(s,object);
+        return object;
+    }
+
+    @Override
+    public Object remove(String s) {
+        return caches.remove(s);
+    }
+
+    @Override
+    public void registerDestructionCallback(String s, Runnable runnable) {
+
+    }
+
+    @Override
+    public Object resolveContextualObject(String s) {
+        return null;
+    }
+
+    @Override
+    public String getConversationId() {
+        return null;
+    }
+}

+ 29 - 0
elab-redis/src/main/java/com/elab/redis/componts/config/scope/RefreshConfigScopeRegistry.java

@@ -0,0 +1,29 @@
+package com.elab.redis.componts.config.scope;
+
+import com.elab.redis.consts.CacheConstants;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
+
+public class RefreshConfigScopeRegistry implements BeanDefinitionRegistryPostProcessor {
+
+    private BeanDefinitionRegistry beanDefinitionRegistry;
+
+    @Override
+    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
+        this.beanDefinitionRegistry = beanDefinitionRegistry;
+    }
+
+    @Override
+    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory)
+        throws BeansException {
+        configurableListableBeanFactory
+            .registerScope(CacheConstants.REFRESH_CONFIG_SCOPE_NAME, new RefreshConfigScope());
+    }
+
+    public BeanDefinitionRegistry getBeanDefinitionRegistry() {
+        return beanDefinitionRegistry;
+    }
+
+}

+ 12 - 0
elab-redis/src/main/java/com/elab/redis/config/ElabRedisProperties.java

@@ -1,5 +1,6 @@
 package com.elab.redis.config;
 
+import com.elab.redis.config.props.ConfigRefreshProperties;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 
 /**
@@ -19,6 +20,8 @@ public class ElabRedisProperties {
      */
     private String prefixWith;
 
+    private ConfigRefreshProperties configRefresh = new ConfigRefreshProperties();
+
     public String getPrefixWith() {
         return prefixWith;
     }
@@ -34,4 +37,13 @@ public class ElabRedisProperties {
     public void setTtl(Integer ttl) {
         this.ttl = ttl;
     }
+
+    public ConfigRefreshProperties getConfigRefresh() {
+        return configRefresh;
+    }
+
+    public void setConfigRefresh(ConfigRefreshProperties configRefresh) {
+        this.configRefresh = configRefresh;
+    }
 }
+

+ 61 - 0
elab-redis/src/main/java/com/elab/redis/config/props/ConfigRefreshProperties.java

@@ -0,0 +1,61 @@
+package com.elab.redis.config.props;
+
+import java.util.Map;
+
+/**
+ * 配置更新
+ *
+ * @author liukaixiong
+ * @Email liukx@elab-plus.com
+ */
+public class ConfigRefreshProperties {
+
+    /**
+     * 是否开启配置更新
+     */
+    private boolean enable = true;
+
+    /**
+     * 配置更新的缓存的key
+     */
+    private String configRefreshCacheKey = "config-refresh";
+
+    /**
+     * 缓存推送的topic的key
+     */
+    private String configPushCacheKey;
+
+    private Map<String, String> defaultConfig;
+
+    public String getConfigRefreshCacheKey() {
+        return configRefreshCacheKey;
+    }
+
+    public void setConfigRefreshCacheKey(String configRefreshCacheKey) {
+        this.configRefreshCacheKey = configRefreshCacheKey;
+    }
+
+    public Map<String, String> getDefaultConfig() {
+        return defaultConfig;
+    }
+
+    public void setDefaultConfig(Map<String, String> defaultConfig) {
+        this.defaultConfig = defaultConfig;
+    }
+
+    public boolean isEnable() {
+        return enable;
+    }
+
+    public void setEnable(boolean enable) {
+        this.enable = enable;
+    }
+
+    public String getConfigPushCacheKey() {
+        return configPushCacheKey;
+    }
+
+    public void setConfigPushCacheKey(String configPushCacheKey) {
+        this.configPushCacheKey = configPushCacheKey;
+    }
+}

+ 6 - 1
elab-redis/src/main/java/com/elab/redis/consts/CacheConstants.java

@@ -8,9 +8,14 @@ package com.elab.redis.consts;
  */
 public class CacheConstants {
 
-
     public final static String ALL = "ALL";
 
     public final static String CACHE_SEPARATOR = ":";
 
+    public final static String REFRESH_CONFIG_SCOPE_REGISTRY = "refreshConfigScopeRegistry";
+    /**
+     * 刷新容器的标记
+     */
+    public final static String REFRESH_CONFIG_SCOPE_NAME = "RefreshConfig";
+
 }

+ 1 - 0
elab-redis/src/main/java/com/elab/redis/interceptor/impl/CacheLoopProcessImpl.java

@@ -67,6 +67,7 @@ public class CacheLoopProcessImpl implements ICacheProcessService {
         RLock lockObject = client.getLock(cacheName);
 
         try {
+            // 最好是看一下分布式锁占用的时长
             if (tryLock(lockObject, cacheLoopSubmit)) {
                 Object proceed = invocation.proceed();
                 return proceed;

+ 29 - 0
elab-redis/src/main/java/com/elab/redis/utils/StringSerializerUtils.java

@@ -0,0 +1,29 @@
+package com.elab.redis.utils;
+
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+public class StringSerializerUtils {
+
+    private static final byte[][] EMPTY_2D_BYTE_ARRAY = new byte[0][];
+
+    private static StringRedisSerializer serializer = new StringRedisSerializer();
+
+    public static byte[] serialize(String data) {
+        return serializer.serialize(data);
+    }
+
+    public static byte[][] serializeMulti(String... keys) {
+
+        if (keys == null) {
+            return EMPTY_2D_BYTE_ARRAY;
+        }
+
+        byte[][] ret = new byte[keys.length][];
+
+        for (int i = 0; i < ret.length; i++) {
+            ret[i] = serializer.serialize(keys[i]);
+        }
+
+        return ret;
+    }
+}

+ 41 - 0
elab-redis/src/test/java/com/elab/redis/componts/config/ConfigCenterRedisStaterTest.java

@@ -0,0 +1,41 @@
+package com.elab.redis.componts.config;
+
+import com.elab.redis.RedissonApplication;
+import com.elab.redis.componts.config.scope.RedisConfigRefreshCallback;
+import com.elab.redis.componts.config.scope.RedisConfigRefreshHelper;
+import junit.framework.TestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = {ConfigController.class, RedisRefreshConfigure.class, RedissonApplication.class})
+//@EnableElabRedis
+public class ConfigCenterRedisStaterTest extends TestCase {
+    private Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Autowired
+    private ConfigController configController;
+
+    @Autowired
+    RedisConfigRefreshCallback refreshConfig;
+
+    @Test
+    public void test() throws Exception {
+        while (true) {
+            // 通过redis 操作 publish test-redis-* xxx
+            // xxx 代表任意字符,主要目的是为了触发监听回调
+            String config = configController.config();
+            logger.info("加载完成。。" + config);
+
+            String username = RedisConfigRefreshHelper.getInstance().get("username");
+            System.out.println("username : " + username);
+            Thread.sleep(5000);
+        }
+    }
+
+}

+ 24 - 0
elab-redis/src/test/java/com/elab/redis/componts/config/ConfigController.java

@@ -0,0 +1,24 @@
+package com.elab.redis.componts.config;
+
+import com.elab.redis.consts.CacheConstants;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Scope;
+import org.springframework.core.env.Environment;
+import org.springframework.stereotype.Service;
+
+@Scope(CacheConstants.REFRESH_CONFIG_SCOPE_NAME)
+@Service
+public class ConfigController {
+
+    @Value("${elab.name}")
+    private String name;
+
+    @Autowired
+    Environment environment;
+
+    public String config() {
+        String re = "====redis.name.@Value==" + name + " @environment===" + environment.getProperty("elab.name");
+        return re;
+    }
+}

+ 38 - 6
elab-redis/src/test/java/com/elab/redis/redisson/RedissonAutoConfigurationTest.java

@@ -1,12 +1,12 @@
 package com.elab.redis.redisson;
 
+import com.elab.core.componts.ConcurrentTool;
 import com.elab.redis.RedissonApplication;
 import com.elab.redis.config.CacheAutoConfiguration;
+import com.google.common.util.concurrent.RateLimiter;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.redisson.api.RAtomicDouble;
-import org.redisson.api.RMap;
-import org.redisson.api.RedissonClient;
+import org.redisson.api.*;
 import org.redisson.client.codec.StringCodec;
 import org.redisson.codec.JsonJacksonCodec;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -15,12 +15,14 @@ import org.springframework.data.redis.core.BoundHashOperations;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.test.context.junit4.SpringRunner;
 
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 import static org.assertj.core.api.Assertions.assertThat;
 
 @RunWith(SpringRunner.class)
-@SpringBootTest(
-        classes = {RedissonApplication.class, CacheAutoConfiguration.class}
-)
+@SpringBootTest(classes = {RedissonApplication.class, CacheAutoConfiguration.class})
 public class RedissonAutoConfigurationTest {
 
     @Autowired
@@ -41,5 +43,35 @@ public class RedissonAutoConfigurationTest {
         assertThat(t).isEqualTo("2");
     }
 
+    @Test
+    public void reteLimiterCase() throws Exception {
+        //        ConcurrentTool concurrentTool = new ConcurrentTool(10);
+        //        //{liukx}
+        //        RRateLimiter rateLimiter = redisson.getRateLimiter("myRateLimiter");
+        //        boolean trySetRate = rateLimiter.trySetRate(RateType.OVERALL, 5, 2, RateIntervalUnit.SECONDS);
+        //        concurrentTool.process(() -> {
+        //
+        //            System.out.println(trySetRate);
+        //            rateLimiter.acquire(1);
+        //            return trySetRate;
+        //        });
+
+        RRateLimiter rateLimiter = redisson.getRateLimiter("abc_{myRateLimiter}");
+
+        if (rateLimiter.isExists()) {
+            // 初始化
+            // 最大流速 = 每1秒钟产生10个令牌
+            rateLimiter.trySetRate(RateType.OVERALL, 10, 1, RateIntervalUnit.SECONDS);
+        }
+        CountDownLatch latch = new CountDownLatch(2);
+        rateLimiter.acquire(3);
+        // ...
+
+        Thread t = new Thread(() -> {
+            rateLimiter.acquire(2);
+            // ...
+        });
+        System.in.read();
+    }
 
 }

+ 1 - 0
elab-redis/src/test/java/com/elab/redis/service/impl/DemoServiceImpl.java

@@ -12,6 +12,7 @@ import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.CachePut;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
 
 /**
  * @author : lukx

+ 91 - 4
elab-redis/src/test/java/com/elab/redis/spring/SpringDataTest.java

@@ -8,7 +8,14 @@ import com.elab.redis.serializers.CompatibilityJsonRedisSerializer;
 import com.google.common.base.Splitter;
 import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
 import org.junit.Test;
+import org.redisson.api.RBitSet;
+import org.redisson.api.RScript;
+import org.redisson.api.RedissonClient;
+import org.redisson.client.codec.StringCodec;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataAccessException;
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.connection.RedisStringCommands;
 import org.springframework.data.redis.core.*;
 import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
 import org.springframework.data.redis.serializer.RedisSerializer;
@@ -33,6 +40,9 @@ public class SpringDataTest extends RedisSpringBoot {
     @Autowired
     private StringRedisTemplate stringRedisTemplate;
 
+    @Autowired
+    private RedissonClient redissonClient;
+
     @Test
     public void serializerTest() {
         RedisTemplate<String, Object> redisTemplate = cacheTemplate.getRedisTemplate();
@@ -129,7 +139,7 @@ public class SpringDataTest extends RedisSpringBoot {
     }
 
     @Test
-    public void delKey(){
+    public void delKey() {
         List<String> delKeyList = new ArrayList<>();
         delKeyList.add("user:userId:mobile:3234916");
         //delKeyList.add("user:userId:mobile:3258428");
@@ -137,8 +147,6 @@ public class SpringDataTest extends RedisSpringBoot {
         System.out.println(delete);
     }
 
-
-
     @Test
     public void del() {
 
@@ -147,7 +155,7 @@ public class SpringDataTest extends RedisSpringBoot {
         // redisTemplate.delete(keys);
         Set<String> mobiles = new HashSet<>();
         List<Object> objects = redisTemplate.opsForValue().multiGet(keys);
-        objects.forEach((V)->{
+        objects.forEach((V) -> {
             mobiles.add(V.toString());
         });
         System.out.println(objects);
@@ -195,4 +203,83 @@ public class SpringDataTest extends RedisSpringBoot {
             (RedisCallback)redisConnection -> new ConvertingCursor<>(redisConnection.scan(scanOptions),
                 keySerializer::deserialize));
     }
+
+    @Test
+    public void bitCase() {
+        String key = "liukx::{login}";
+
+        //        boolean b = cacheTemplate.bitAdd(key, 1);
+        //        System.out.println(b);
+        //        System.out.println("10010是否存在 : " + cacheTemplate.bitGet(key, 1));
+        //        System.out.println("10000是否存在 : " + cacheTemplate.bitGet(key, 2));
+
+        String key2 = "jay::{login}";
+
+        boolean b2 = cacheTemplate.bitAdd(key2, 2);
+        System.out.println(b2);
+        System.out.println("10010是否存在 : " + cacheTemplate.bitGet(key2, 1));
+        System.out.println("10000是否存在 : " + cacheTemplate.bitGet(key2, 2));
+        //        boolean b2 =
+        //            cacheTemplate.bitAndGet("jay_liukx_result::{login}", initNumber + 1, Arrays.asList(key, "jay::{login}"));
+        //        System.out.println("合并后的结果: " + b2);
+
+    }
+
+    @Test
+    public void bitAnd() {
+        String newKey = "result::{login}";
+        String key1 = "jay::{login}";
+        String key2 = "liukx::{login}";
+
+        // 先初始化
+        cacheTemplate.bitAdd(key1, 1);
+        cacheTemplate.bitAdd(key1, 2);
+        cacheTemplate.bitAdd(key2, 2);
+
+        System.out.println(cacheTemplate.bitGet(key1, 1));
+        System.out.println(cacheTemplate.bitGet(key2, 2));
+
+        cacheTemplate.bitBiTop(RedisStringCommands.BitOperation.AND, newKey, Arrays.asList(key1, key2));
+
+        System.out.println(cacheTemplate.bitGet(newKey, 2));
+
+        //        RBitSet bitSet = redissonClient.getBitSet(key2);
+        //        bitSet.rename("key_result::{login}");
+
+        //        bitSet.and(key1);
+        //        System.out.println(bitSet.get(2));
+        //        boolean b2 = cacheTemplate.bitAndGet("key2::{login}", 1, Arrays.asList(key2, key1));
+        //        System.out.println("合并后的结果: " + b2);
+        //                RBitSet bitSet = redissonClient.getBitSet("abc::{login}");
+        //                System.out.println(redisTemplate.hasKey("abc::{login}"));
+    }
+
+    @Test
+    public void testScript() {
+        RScript script = redissonClient.getScript();
+        RBitSet bitSet = redissonClient.getBitSet("");
+        // execute script in read only mode
+        String result =
+            script.eval(RScript.Mode.READ_ONLY, "return redis.call('get', 'foo')", RScript.ReturnType.BOOLEAN);
+
+        // execute the same script stored in Redis lua script cache
+
+        // load lua script into Redis cache to all redis master instances
+        String sha1 = script.scriptLoad("return redis.call('get', 'foo')");
+    }
+
+    @Test
+    public void testHyperLogLog() {
+        System.out.println(cacheTemplate.hyperLogCount("liukx"));
+
+        System.out.println(cacheTemplate.hyperLogAdd("liukx", "jay"));
+        System.out.println(cacheTemplate.hyperLogAdd("liukx", "jj"));
+        System.out.println(cacheTemplate.hyperLogAdd("liukx", "jj"));
+        System.out.println(cacheTemplate.hyperLogAdd("liukx", "jj"));
+        System.out.println(cacheTemplate.hyperLogAdd("liukx", "vae"));
+
+        System.out.println(cacheTemplate.hyperLogAdd("liukx", "vae"));
+        System.out.println(cacheTemplate.hyperLogCount("liukx"));
+    }
+
 }

+ 18 - 9
elab-redis/src/test/java/com/elab/redis/utils/TestUtils.java

@@ -17,15 +17,10 @@ public class TestUtils {
 
     @Test
     public void testHashCode() {
-        String json = " \t转换后的sql -\n" +
-                "select\n" +
-                "COUNT(DISTINCT created) as days\n" +
-                "from\n" +
-                "t_zhidi_activity_info\n" +
-                "where brand_id = ? and house_id = ? and activity_id = ?\n" +
-                "and activity_type\n" +
-                "= ? and recommend_customer_id = ? and `status` = 1\n" +
-                "and created >= ? and created <= ?1";
+        String json =
+            " \t转换后的sql -\n" + "select\n" + "COUNT(DISTINCT created) as days\n" + "from\n" + "t_zhidi_activity_info\n"
+                + "where brand_id = ? and house_id = ? and activity_id = ?\n" + "and activity_type\n"
+                + "= ? and recommend_customer_id = ? and `status` = 1\n" + "and created >= ? and created <= ?1";
         String a = "Aa";
         String b = "BB";
         int aa = a.hashCode();
@@ -63,4 +58,18 @@ public class TestUtils {
         }
 
     }
+
+    @Test
+    public void testShorUrl() {
+        int initCount = 1231212212;
+        StringBuilder stringBuilder = new StringBuilder();
+        String[] X36_ARRAY = "0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z".split(",");
+        while (initCount > 0) {
+            stringBuilder.append(X36_ARRAY[initCount % 36]);
+            initCount = initCount / 36;
+        }
+
+        String url = stringBuilder.reverse().toString();
+        System.out.println(url);
+    }
 }

+ 10 - 1
elab-redis/src/test/resources/application.yml

@@ -1,6 +1,6 @@
 spring:
   redis:
-    database: 10
+    database: 6
     host: r-uf6e60u5cmlj0sx563pd.redis.rds.aliyuncs.com
     password: xcGm4kTks6
     port: 6379
@@ -8,5 +8,14 @@ spring:
     elab:
       ttl: 30
       prefix-with: user_
+      config-refresh:
+        default-config:
+          username: xiong
+          company: elab
   application:
     name: test-redis
+
+#debug: true
+
+elab:
+  name: lkx

+ 5 - 4
elab-spring/src/main/java/com/elab/spring/anno/MQOperation.java

@@ -2,6 +2,7 @@ package com.elab.spring.anno;
 
 import com.elab.annotation.Author;
 import com.elab.annotation.Description;
+import com.elab.core.aop.annotations.ExceptionHandle;
 import org.springframework.core.annotation.AliasFor;
 
 import java.lang.annotation.*;
@@ -17,13 +18,13 @@ import java.lang.annotation.*;
 @Documented
 @Inherited
 @Author
+@ExceptionHandle
 @Description
 public @interface MQOperation {
 
-    @AliasFor(annotation = Author.class, attribute = "nickname")
-    String[] nickname() default {};
+    @AliasFor(annotation = Author.class, attribute = "nickname") String[] nickname() default {};
 
-    @AliasFor(annotation = Description.class, attribute = "remark")
-    String remark() default "";
+
+    @AliasFor(annotation = Description.class, attribute = "remark") String remark() default "";
 
 }

+ 16 - 0
elab-spring/src/main/java/com/elab/spring/componts/conig/ConfigRefresh.java

@@ -0,0 +1,16 @@
+package com.elab.spring.componts.conig;
+
+/**
+ * 描述:
+ * 配置刷新接口
+ *
+ * @author liukx
+ * @date 2021/8/10 17:42
+ */
+public interface ConfigRefresh {
+
+    public String get(String key);
+
+    public <T> T get(String key, Class<T> clazz);
+
+}

+ 44 - 0
elab-spring/src/main/java/com/elab/spring/componts/conig/ConfigRefreshHelper.java

@@ -0,0 +1,44 @@
+package com.elab.spring.componts.conig;
+
+import com.elab.spring.utils.SpringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 配置刷新获取
+ *
+ * @author liukaixiong
+ * @Email liukx@elab-plus.com
+ */
+public class ConfigRefreshHelper implements ConfigRefresh {
+
+    private Logger logger = LoggerFactory.getLogger(getClass());
+
+    private static ConfigRefreshHelper INSTANCE = new ConfigRefreshHelper();
+
+    public static ConfigRefreshHelper getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public String get(String key) {
+        try {
+            ConfigRefresh configRefresh = SpringUtils.getBean(ConfigRefresh.class);
+            return configRefresh.get(key);
+        } catch (Exception e) {
+            logger.debug(e.getMessage());
+        }
+        return null;
+    }
+
+    @Override
+    public <T> T get(String key, Class<T> clazz) {
+        try {
+            ConfigRefresh configRefresh = SpringUtils.getBean(ConfigRefresh.class);
+            return configRefresh.get(key, clazz);
+        } catch (Exception e) {
+            logger.debug(e.getMessage());
+        }
+        return null;
+    }
+}

+ 22 - 0
elab-spring/src/main/java/com/elab/spring/componts/conig/RedisConfigRefresh.java

@@ -0,0 +1,22 @@
+package com.elab.spring.componts.conig;
+
+import com.elab.redis.componts.config.scope.RedisConfigRefreshHelper;
+
+/**
+ * redis的配置刷新
+ *
+ * @author liukaixiong
+ * @Email liukx@elab-plus.com
+ */
+public class RedisConfigRefresh implements ConfigRefresh {
+
+    @Override
+    public String get(String key) {
+        return RedisConfigRefreshHelper.getInstance().get(key);
+    }
+
+    @Override
+    public <T> T get(String key, Class<T> clazz) {
+        return RedisConfigRefreshHelper.getInstance().get(key, clazz);
+    }
+}

+ 54 - 0
elab-spring/src/main/java/com/elab/spring/componts/containers/ContainerRefresh.java

@@ -0,0 +1,54 @@
+package com.elab.spring.componts.containers;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.ApplicationArguments;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ContainerRefresh {
+
+    private Logger logger = LoggerFactory.getLogger(getClass());
+    private volatile boolean isRefresh = false;
+    private List<SupplierVoid> invokeMethodList = new ArrayList<>();
+    private int callCount = 0;
+
+    private static ContainerRefresh INSTACE = new ContainerRefresh();
+
+    public static ContainerRefresh getInstance() {
+        return INSTACE;
+    }
+
+    public void register(SupplierVoid supplier) {
+        invokeMethodList.add(supplier);
+    }
+
+    public boolean isRefresh() {
+        return isRefresh;
+    }
+
+    protected void refreshMethod(ApplicationArguments args) {
+
+        if (isRefresh()) {
+            callCount++;
+            logger.warn("请注意存在重复刷新容器方法的情况! >> " + callCount);
+        }
+
+        invokeMethodList.forEach((method) -> {
+            logger.debug(">>>>>>>>>>" + method + "<<<<<<<<<<");
+            method.process(args);
+            logger.debug("<<<<<<<<<<" + method + ">>>>>>>>>>");
+        });
+        isRefresh = true;
+    }
+
+    public interface SupplierVoid {
+        /**
+         * Gets a result.
+         *
+         * @return a result
+         */
+        void process(ApplicationArguments args);
+    }
+}

+ 28 - 0
elab-spring/src/main/java/com/elab/spring/componts/containers/ContainerRefreshRunner.java

@@ -0,0 +1,28 @@
+package com.elab.spring.componts.containers;
+
+import com.elab.core.exception.BusinessException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.ApplicationArguments;
+import org.springframework.boot.ApplicationRunner;
+
+/**
+ * 容器刷新完成回调
+ *
+ * @author liukaixiong
+ * @Email liukx@elab-plus.com
+ */
+public class ContainerRefreshRunner implements ApplicationRunner {
+    protected Logger logger = LoggerFactory.getLogger(getClass());
+
+    @Override
+    public void run(ApplicationArguments args) throws Exception {
+        logger.info("===============================容器刷新完成,开启回调方法====================================");
+        ContainerRefresh.getInstance().refreshMethod(args);
+        logger.info("  ");
+
+        BusinessException adfasdf = new BusinessException("adfasdf");
+        logger.error("error",adfasdf);
+
+    }
+}

+ 27 - 0
elab-spring/src/main/java/com/elab/spring/config/ConfigRefreshAutoConfig.java

@@ -0,0 +1,27 @@
+package com.elab.spring.config;
+
+import com.elab.redis.componts.config.scope.RedisConfigRefreshCallback;
+import com.elab.spring.componts.conig.ConfigRefresh;
+import com.elab.spring.componts.conig.RedisConfigRefresh;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 自动配置
+ *
+ * @author liukaixiong
+ * @Email liukx@elab-plus.com
+ */
+@Configuration
+public class ConfigRefreshAutoConfig {
+
+    @Bean
+    @ConditionalOnBean(value = {RedisConfigRefreshCallback.class})
+    @ConditionalOnProperty(prefix = "elab.spring", value = "config-refresh", havingValue = "REDIS")
+    public ConfigRefresh configRefresh() {
+        return new RedisConfigRefresh();
+    }
+
+}

+ 38 - 0
elab-spring/src/main/java/com/elab/spring/config/prop/SpringAutoProperties.java

@@ -0,0 +1,38 @@
+package com.elab.spring.config.prop;
+
+import com.elab.spring.enums.ConfigRefreshEnum;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+
+/**
+ * @author liukaixiong
+ * @Email liukx@elab-plus.com
+ */
+@ConditionalOnProperty(prefix = "elab.spring")
+public class SpringAutoProperties {
+
+    /**
+     * 配置刷新
+     */
+    private ConfigRefreshEnum configRefresh;
+
+    /**
+     * 线程属性
+     */
+    private ThreadProperties thread = new ThreadProperties();
+
+    public ConfigRefreshEnum getConfigRefresh() {
+        return configRefresh;
+    }
+
+    public void setConfigRefresh(ConfigRefreshEnum configRefresh) {
+        this.configRefresh = configRefresh;
+    }
+
+    public ThreadProperties getThread() {
+        return thread;
+    }
+
+    public void setThread(ThreadProperties thread) {
+        this.thread = thread;
+    }
+}

+ 0 - 2
elab-spring/src/main/java/com/elab/spring/config/prop/ThreadProperties.java

@@ -8,8 +8,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
  * @author : liukx
  * @time : 2020/8/14 - 14:19
  */
-
-@ConfigurationProperties(prefix = "spring.elab.thread")
 public class ThreadProperties {
 
     /**

+ 7 - 0
elab-spring/src/main/java/com/elab/spring/enums/ConfigRefreshEnum.java

@@ -0,0 +1,7 @@
+package com.elab.spring.enums;
+
+public enum ConfigRefreshEnum {
+    REDIS,
+    NACOS,
+    ZK;
+}

+ 21 - 21
elab-spring/src/main/java/com/elab/spring/utils/RestTemplateUtils.java

@@ -172,7 +172,8 @@ public class RestTemplateUtils {
      * @param <T>
      * @return
      */
-    public <T> T post(String url, HttpHeaders httpHeaders, Object reqParam, Class<T> clazz, IRestFallback<?> restFallback) {
+    public <T> T post(String url, HttpHeaders httpHeaders, Object reqParam, Class<T> clazz,
+        IRestFallback<?> restFallback) {
         String newUrl = getUrl(url);
         Transaction t = Cat.getProducer().newTransaction(CatMsgConstants.THIRD_PARTY, newUrl);
         logger.debug(" URL : " + url);
@@ -196,12 +197,12 @@ public class RestTemplateUtils {
         } catch (Exception e) {
             e.printStackTrace();
             if (!(e instanceof CoolingException)) {
-                logger.warn("第三方调用异常:", e);
+                logger.warn("第三方调用异常:" + e.getMessage());
             }
             t.setStatus(e.getClass().getSimpleName());
             if (restFallback != null) {
                 logger.debug("触发异常回调 : " + restFallback.toString());
-                return (T) restFallback.post(url, httpHeaders, reqParam, clazz, e);
+                return (T)restFallback.post(url, httpHeaders, reqParam, clazz, e);
             }
         } finally {
             t.complete();
@@ -237,13 +238,15 @@ public class RestTemplateUtils {
         return exchange(url, HttpMethod.PUT, reqParam, clazz, httpHeaders, null);
     }
 
-    private <T> void requestAfter(String url, HttpHeaders httpHeaders, Object reqParam, Class<T> clazz, IRestFallback<?> restFallback) throws Exception {
+    private <T> void requestAfter(String url, HttpHeaders httpHeaders, Object reqParam, Class<T> clazz,
+        IRestFallback<?> restFallback) throws Exception {
         if (restFallback != null) {
             restFallback.requestAfter(url, httpHeaders, reqParam, clazz);
         }
     }
 
-    private <T> void requestBefore(String url, HttpHeaders httpHeaders, Object reqParam, Class<T> clazz, IRestFallback<?> restFallback) throws Exception {
+    private <T> void requestBefore(String url, HttpHeaders httpHeaders, Object reqParam, Class<T> clazz,
+        IRestFallback<?> restFallback) throws Exception {
         if (restFallback != null) {
             restFallback.requestBefore(url, httpHeaders, reqParam, clazz);
         }
@@ -285,16 +288,16 @@ public class RestTemplateUtils {
             responseData = restTemplate.getForObject(url, clazz);
             t.setStatus(Transaction.SUCCESS);
             if (responseData != null) {
-                logResponse((T) responseData);
+                logResponse((T)responseData);
             }
         } catch (Exception e) {
             if (!(e instanceof CoolingException)) {
-                logger.warn("第三方调用异常:", e);
+                logger.warn("第三方调用异常:"+e.getMessage());
             }
             t.setStatus(e.getClass().getSimpleName());
             if (restFallback != null) {
                 logger.debug("触发失败回调 : " + restFallback.toString());
-                return (T) restFallback.get(url, clazz, e);
+                return (T)restFallback.get(url, clazz, e);
             }
         } finally {
             t.complete();
@@ -339,7 +342,8 @@ public class RestTemplateUtils {
         return exchange(url, HttpMethod.GET, body, clazz, httpHeaders, restFallback);
     }
 
-    private <T> T exchange(String url, HttpMethod httpMethod, Object body, Class<T> clazz, HttpHeaders httpHeaders, IRestFallback restFallback) {
+    private <T> T exchange(String url, HttpMethod httpMethod, Object body, Class<T> clazz, HttpHeaders httpHeaders,
+        IRestFallback restFallback) {
         String newUrl = getUrl(url);
         Transaction t = Cat.getProducer().newTransaction(CatMsgConstants.THIRD_PARTY, newUrl);
         logger.debug(" URL : " + url);
@@ -360,16 +364,16 @@ public class RestTemplateUtils {
             responseData = result.getBody();
             t.setStatus(Transaction.SUCCESS);
             if (responseData != null) {
-                logResponse((T) responseData);
+                logResponse((T)responseData);
             }
         } catch (Exception e) {
             if (!(e instanceof CoolingException)) {
-                logger.warn("第三方调用异常:", e);
+                logger.warn("第三方调用异常:"+ e.getMessage());
             }
             t.setStatus(e.getClass().getSimpleName());
             if (restFallback != null) {
                 logger.debug("触发失败回调 : " + restFallback.toString());
-                return (T) restFallback.get(url, clazz, e);
+                return (T)restFallback.get(url, clazz, e);
             }
         } finally {
             t.complete();
@@ -407,7 +411,7 @@ public class RestTemplateUtils {
             }
             t.setStatus(Transaction.SUCCESS);
         } catch (Exception e) {
-            logger.warn("------ 第三方接口调用失败 : ", e);
+            logger.warn("------ 第三方接口调用失败 : "+ e.getMessage());
             t.setStatus(e.getClass().getSimpleName());
         } finally {
             t.complete();
@@ -450,16 +454,12 @@ public class RestTemplateUtils {
     }
 
     public static void main(String[] args) {
-        String param = "{\n" +
-                "\t\"pageno\":\"1\",\n" +
-                "\t\"pagesize\":\"10\",\n" +
-                "\t\"houseid\":\"102\",\n" +
-                "\t\"time\":\"2017-04-13\"\n" +
-                "}";
+        String param = "{\n" + "\t\"pageno\":\"1\",\n" + "\t\"pagesize\":\"10\",\n" + "\t\"houseid\":\"102\",\n"
+            + "\t\"time\":\"2017-04-13\"\n" + "}";
         JSONObject jsonObject = JSON.parseObject(param);
         RestTemplateUtils restTemplateUtils = new RestTemplateUtils();
-//        JSONModel post = restTemplateUtils.post("http://localhost:9005/skyforest/dd/pageList", jsonObject, JSONModel.class);
-//        System.out.println(JSON.toJSONString(post));
+        //        JSONModel post = restTemplateUtils.post("http://localhost:9005/skyforest/dd/pageList", jsonObject, JSONModel.class);
+        //        System.out.println(JSON.toJSONString(post));
     }
 
 }

+ 40 - 0
elab-spring/src/main/java/com/elab/spring/utils/SpringEventUtils.java

@@ -0,0 +1,40 @@
+package com.elab.spring.utils;
+
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ApplicationEventPublisherAware;
+
+public class SpringEventUtils implements ApplicationEventPublisherAware {
+
+    private static ApplicationEventPublisher applicationEventPublisher;
+
+    @Override
+    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
+        this.applicationEventPublisher = applicationEventPublisher;
+    }
+
+    //    /**
+    //     * 同步推送event事件
+    //     *
+    //     * @param applicationEvent 事件来源
+    //     */
+    //    public static void syncEventPublisher(ApplicationEvent applicationEvent) {
+    //        applicationEventPublisher.publishEvent(applicationEvent);
+    //    }
+
+    public static void syncEventPublisher(ApplicationEvent applicationEvent) {
+        applicationEventPublisher.publishEvent(applicationEvent);
+    }
+
+    /**
+     * 异步推送event事件
+     *
+     * @param applicationEvent 事件来源
+     */
+    public static void asyncEventPublisher(ApplicationEvent applicationEvent) {
+        ThreadProcessUtils.addRealTimeQueue(() -> {
+            applicationEventPublisher.publishEvent(applicationEvent);
+        }, null);
+    }
+
+}

+ 32 - 0
elab-spring/src/main/java/com/elab/spring/utils/ThreadProcessUtils.java

@@ -86,6 +86,20 @@ public class ThreadProcessUtils implements ApplicationContextAware, Initializing
         return supplyAsync(true, null, supplier);
     }
 
+    /**
+     * 异步任务列表
+     *
+     * @param isBlockResult 是否阻塞获取结果
+     * @param supplier
+     * @param <U>
+     * @return
+     * @throws Exception
+     */
+    public static <U> CompletableFuture<U>[] supplyAsync(boolean isBlockResult, Supplier<U>... supplier)
+        throws Exception {
+        return supplyAsync(isBlockResult, null, supplier);
+    }
+
     /**
      * 多任务并行处理,最终按照提交顺序返回
      *
@@ -155,6 +169,24 @@ public class ThreadProcessUtils implements ApplicationContextAware, Initializing
         return resultMap;
     }
 
+    /**
+     * 将多任务根据key绑定执行,并按照key返回对应的任务组
+     *
+     * @param taskGroupMap
+     * @param <U>
+     * @return
+     * @throws Exception
+     */
+    public static <U> Map<String, U> supplyAsyncByMap(Map<String, Supplier<U>> taskGroupMap) throws Exception {
+        Map<String, CompletableFuture<U>> completableFutureMap = supplyAsync(taskGroupMap);
+        Map<String, U> resultMap = new HashMap<>();
+        for (Map.Entry<String, CompletableFuture<U>> entry : completableFutureMap.entrySet()) {
+            U result = entry.getValue().get();
+            resultMap.put(entry.getKey(), result);
+        }
+        return resultMap;
+    }
+
     private static <U> void allOf(boolean isBlockResult, Long timeOutMs, CompletableFuture<U>[] result)
         throws InterruptedException, ExecutionException, TimeoutException {
         if (isBlockResult) {

+ 2 - 1
elab-spring/src/test/java/com/elab/spring/anno/TestAnnotation.java

@@ -1,6 +1,7 @@
 package com.elab.spring.anno;
 
 import com.elab.annotation.Author;
+import com.elab.core.aop.annotations.ExceptionHandle;
 import org.junit.Test;
 import org.springframework.core.annotation.AnnotatedElementUtils;
 
@@ -23,7 +24,7 @@ public class TestAnnotation {
     @Test
     public void test() throws Exception {
         Method test1 = TestAnnotation.class.getDeclaredMethod("test1", null);
-        Author mergedAnnotation = AnnotatedElementUtils.findMergedAnnotation(test1, Author.class);
+        ExceptionHandle mergedAnnotation = AnnotatedElementUtils.findMergedAnnotation(test1, ExceptionHandle.class);
 
         System.out.println(mergedAnnotation);
 

+ 60 - 0
elab-spring/src/test/java/com/elab/spring/design/group/AbstractInvocationProcess.java

@@ -0,0 +1,60 @@
+package com.elab.spring.design.group;
+
+import com.elab.spring.utils.ThreadProcessUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Supplier;
+
+public abstract class AbstractInvocationProcess<T, R extends GroupResult> {
+
+    @Autowired
+    private GroupInvocationManager groupInvocationManager;
+
+    protected boolean preHandler(T req) {
+        return true;
+    }
+
+    public abstract String groupName();
+
+    public abstract R postHandler(T req, Map<String, Object> res);
+
+    public R invoke(T req) throws Exception {
+
+        String groupName = groupName();
+
+        if (!this.preHandler(req)) {
+            return null;
+        }
+
+        List<GroupRouteInvocation> invocationList = groupInvocationManager.getInvocationList(groupName);
+
+        Map<String, Supplier<Object>> threadResultMap = new HashMap<>();
+
+        for (int i = 0; i < invocationList.size(); i++) {
+            GroupRouteInvocation groupRouteInvocation = invocationList.get(i);
+            String unionKey = groupRouteInvocation.unionKey();
+            if (groupRouteInvocation instanceof SubscribeInvocation) {
+                SubscribeInvocation subscribeInvocation = (SubscribeInvocation)groupRouteInvocation;
+
+                if (!subscribeInvocation.preHandler(req)) {
+                    continue;
+                }
+            }
+
+            threadResultMap.put(unionKey, () -> {
+                return groupRouteInvocation.invoke(req);
+            });
+
+        }
+
+        Map<String, Object> resultMap = ThreadProcessUtils.supplyAsyncByMap(threadResultMap);
+
+        return postHandler(req, resultMap);
+    }
+
+}
+
+

+ 14 - 0
elab-spring/src/test/java/com/elab/spring/design/group/GroupAdaptor.java

@@ -0,0 +1,14 @@
+package com.elab.spring.design.group;
+
+public abstract class GroupAdaptor<T, R> implements GroupRouteInvocation<T, R>, SubscribeInvocation<T, R> {
+
+    @Override
+    public boolean preHandler(T req) {
+        return true;
+    }
+
+    @Override
+    public void postHandler(T req, R res) {
+
+    }
+}

+ 53 - 0
elab-spring/src/test/java/com/elab/spring/design/group/GroupInvocationManager.java

@@ -0,0 +1,53 @@
+package com.elab.spring.design.group;
+
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Component
+public class GroupInvocationManager implements InitializingBean {
+
+    @Autowired
+    private List<GroupRouteInvocation> groupRouteInvocationList;
+
+    private Map<String, Map<String, GroupRouteInvocation>> groupMap = new HashMap<>();
+
+    public List<GroupRouteInvocation> getInvocationList(String groupName) {
+        Map<String, GroupRouteInvocation> groupRouteInvocationMap = groupMap.get(groupName);
+
+        if (groupRouteInvocationMap == null) {
+            return null;
+        }
+        return groupRouteInvocationMap.values().stream().collect(Collectors.toList());
+    }
+
+    public GroupRouteInvocation getInvocation(String groupName, String key) {
+        Map<String, GroupRouteInvocation> groupRouteInvocationMap = groupMap.get(groupName);
+        if (groupRouteInvocationMap != null) {
+            return groupRouteInvocationMap.get(key);
+        }
+        return null;
+    }
+
+    @Override
+    public void afterPropertiesSet() throws Exception {
+        if (this.groupRouteInvocationList != null) {
+            this.groupRouteInvocationList.forEach(obj -> {
+                Map<String, GroupRouteInvocation> routeInvocationMap = groupMap.get(obj.groupName());
+                if (routeInvocationMap == null) {
+                    routeInvocationMap = new HashMap<>();
+                    groupMap.put(obj.groupName(), routeInvocationMap);
+                }
+
+                if (obj.unionKey() != null) {
+                    routeInvocationMap.put(obj.unionKey(), obj);
+                }
+            });
+        }
+    }
+}

+ 24 - 0
elab-spring/src/test/java/com/elab/spring/design/group/GroupResult.java

@@ -0,0 +1,24 @@
+package com.elab.spring.design.group;
+
+public class GroupResult {
+
+    private String groupName;
+
+    private String unionKey;
+
+    public String getGroupName() {
+        return groupName;
+    }
+
+    public void setGroupName(String groupName) {
+        this.groupName = groupName;
+    }
+
+    public String getUnionKey() {
+        return unionKey;
+    }
+
+    public void setUnionKey(String unionKey) {
+        this.unionKey = unionKey;
+    }
+}

+ 25 - 0
elab-spring/src/test/java/com/elab/spring/design/group/GroupRouteInvocation.java

@@ -0,0 +1,25 @@
+package com.elab.spring.design.group;
+
+public interface GroupRouteInvocation<T, R> {
+    /**
+     * 分组名称
+     *
+     * @return
+     */
+    public String groupName();
+
+    /**
+     * 唯一的key标识
+     *
+     * @return
+     */
+    public String unionKey();
+
+    /**
+     * 执行方法
+     *
+     * @param req
+     * @return
+     */
+    public R invoke(T req);
+}

+ 9 - 0
elab-spring/src/test/java/com/elab/spring/design/group/SubscribeInvocation.java

@@ -0,0 +1,9 @@
+package com.elab.spring.design.group;
+
+public interface SubscribeInvocation<T, R> {
+
+    public boolean preHandler(T req);
+
+    public void postHandler(T req, R res);
+
+}

+ 7 - 0
elab-spring/src/test/java/com/elab/spring/design/group/test/GroupConstant.java

@@ -0,0 +1,7 @@
+package com.elab.spring.design.group.test;
+
+public class GroupConstant {
+
+    public final static String TEST = "TEST";
+
+}

+ 27 - 0
elab-spring/src/test/java/com/elab/spring/design/group/test/Main.java

@@ -0,0 +1,27 @@
+package com.elab.spring.design.group.test;
+
+import com.elab.spring.config.ThreadConfiguration;
+import com.elab.spring.design.group.GroupInvocationManager;
+import com.elab.spring.design.group.test.group.AGroup;
+import com.elab.spring.design.group.test.group.BGroup;
+import com.elab.spring.design.group.test.group.CGroup;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = {TestGroupProcess.class, GroupInvocationManager.class, AGroup.class, BGroup.class,
+    CGroup.class, ThreadConfiguration.class})
+public class Main {
+
+    @Autowired
+    private TestGroupProcess testGroupProcess;
+
+    @Test
+    public void test() throws Exception {
+        testGroupProcess.invoke("lkx");
+    }
+
+}

+ 22 - 0
elab-spring/src/test/java/com/elab/spring/design/group/test/TestGroupProcess.java

@@ -0,0 +1,22 @@
+package com.elab.spring.design.group.test;
+
+import com.elab.spring.design.group.AbstractInvocationProcess;
+import com.elab.spring.design.group.GroupResult;
+
+import java.util.Map;
+
+public class TestGroupProcess extends AbstractInvocationProcess<String, GroupResult> {
+
+    @Override
+    public String groupName() {
+        return GroupConstant.TEST;
+    }
+
+    @Override
+    public GroupResult postHandler(String req, Map<String, Object> res) {
+        System.out.println("结果汇总:" + res.toString());
+        GroupResult groupResult = new GroupResult();
+        groupResult.setGroupName(groupName());
+        return groupResult;
+    }
+}

+ 25 - 0
elab-spring/src/test/java/com/elab/spring/design/group/test/group/AGroup.java

@@ -0,0 +1,25 @@
+package com.elab.spring.design.group.test.group;
+
+import com.elab.spring.design.group.GroupAdaptor;
+import com.elab.spring.design.group.test.GroupConstant;
+import org.springframework.stereotype.Component;
+
+@Component
+public class AGroup extends GroupAdaptor<String, Integer> {
+
+    @Override
+    public String groupName() {
+        return GroupConstant.TEST;
+    }
+
+    @Override
+    public String unionKey() {
+        return "A";
+    }
+
+    @Override
+    public Integer invoke(String req) {
+        System.out.println(this.getClass().getSimpleName() + "执行完了 : " + req);
+        return 1;
+    }
+}

+ 25 - 0
elab-spring/src/test/java/com/elab/spring/design/group/test/group/BGroup.java

@@ -0,0 +1,25 @@
+package com.elab.spring.design.group.test.group;
+
+import com.elab.spring.design.group.GroupAdaptor;
+import com.elab.spring.design.group.test.GroupConstant;
+import org.springframework.stereotype.Component;
+
+@Component
+public class BGroup extends GroupAdaptor<String, String> {
+
+    @Override
+    public String groupName() {
+        return GroupConstant.TEST;
+    }
+
+    @Override
+    public String unionKey() {
+        return "B";
+    }
+
+    @Override
+    public String invoke(String req) {
+        System.out.println(this.getClass().getSimpleName() + "执行完了 : " + req);
+        return "YES";
+    }
+}

+ 25 - 0
elab-spring/src/test/java/com/elab/spring/design/group/test/group/CGroup.java

@@ -0,0 +1,25 @@
+package com.elab.spring.design.group.test.group;
+
+import com.elab.spring.design.group.GroupAdaptor;
+import com.elab.spring.design.group.test.GroupConstant;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CGroup extends GroupAdaptor<String, Integer> {
+
+    @Override
+    public String groupName() {
+        return GroupConstant.TEST;
+    }
+
+    @Override
+    public String unionKey() {
+        return "C";
+    }
+
+    @Override
+    public Integer invoke(String req) {
+        System.out.println(this.getClass().getSimpleName() + "执行完了 : " + req);
+        return 3;
+    }
+}

+ 17 - 0
elab-spring/src/test/java/com/elab/spring/utils/Source2ApplicationListener.java

@@ -0,0 +1,17 @@
+package com.elab.spring.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.elab.spring.utils.event.EventA;
+import org.springframework.context.ApplicationListener;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+
+import java.lang.annotation.Annotation;
+
+public class Source2ApplicationListener implements ApplicationListener<EventA> {
+
+    @Override
+    public void onApplicationEvent(EventA event) {
+        System.out.println("SourceApplicationListener2 消费到: " + JSON.toJSONString(event.getSource()));
+    }
+}

+ 14 - 0
elab-spring/src/test/java/com/elab/spring/utils/Source3ApplicationListener.java

@@ -0,0 +1,14 @@
+package com.elab.spring.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.elab.spring.utils.event.EventA;
+import com.elab.spring.utils.event.EventB;
+import org.springframework.context.ApplicationListener;
+
+public class Source3ApplicationListener implements ApplicationListener<EventB> {
+
+    @Override
+    public void onApplicationEvent(EventB event) {
+        System.out.println("Source3ApplicationListener 消费到: " + JSON.toJSONString(event.getSource()));
+    }
+}

+ 19 - 0
elab-spring/src/test/java/com/elab/spring/utils/SourceApplicationListener.java

@@ -0,0 +1,19 @@
+package com.elab.spring.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.elab.spring.utils.event.EventA;
+import org.springframework.context.ApplicationListener;
+import org.springframework.core.Ordered;
+
+public class SourceApplicationListener implements ApplicationListener<EventA>, Ordered {
+
+    @Override
+    public int getOrder() {
+        return 0;  // 数值越低优先级越高
+    }
+
+    @Override
+    public void onApplicationEvent(EventA event) {
+        System.out.println("SourceApplicationListener 消费到: " + JSON.toJSONString(event.getSource()));
+    }
+}

+ 119 - 0
elab-spring/src/test/java/com/elab/spring/utils/SpringCase.java

@@ -0,0 +1,119 @@
+package com.elab.spring.utils;
+
+import com.elab.spring.utils.event.SourceModel;
+import com.xiaoleilu.hutool.lang.ClassScaner;
+import org.junit.Test;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
+import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.DefaultResourceLoader;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+import org.springframework.core.io.support.PropertiesLoaderUtils;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.core.io.support.ResourcePatternUtils;
+import org.springframework.core.type.filter.AnnotationTypeFilter;
+import org.springframework.core.type.filter.AssignableTypeFilter;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.util.PatternMatchUtils;
+import org.springframework.util.ResourceUtils;
+
+import javax.xml.bind.SchemaOutputResolver;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+public class SpringCase {
+
+    @Test
+    public void resourceCase() throws FileNotFoundException {
+        File clsFile = ResourceUtils.getFile("classpath:application.yml");
+        System.out.println(clsFile);
+    }
+
+    @Test
+    public void propertiesCase() throws Exception {
+        Properties res = PropertiesLoaderUtils.loadProperties(new ClassPathResource("/META-INF/app.properties"));
+        System.out.println(" loadProperties :" + res);
+
+        ClassPathResource classPathResource = new ClassPathResource("/META-INF/app.properties");
+
+        Properties ret = new Properties();
+        PropertiesLoaderUtils.fillProperties(ret, classPathResource);
+        System.out.println(" fillProperties :" + ret);
+    }
+
+    @Test
+    public void resourcePattern() throws IOException {
+        //
+        PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
+        Resource[] resources = resourcePatternResolver.getResources("classpath:*.yml");
+        System.out.println(resources);
+        Set<Class<?>> classes = ClassScaner.scanPackage("com.elab.spring.utils.event");
+
+        Resource[] resources1 =
+            ResourcePatternUtils.getResourcePatternResolver(new DefaultResourceLoader()).getResources("mapper/*");
+        System.out.println(resources1);
+    }
+
+    @Test
+    public void classScanCase() {
+        // true:默认TypeFilter生效,这种模式会查询出许多不符合你要求的class名
+        // false:关闭默认TypeFilter
+        ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
+
+        // 扫描带有自定义注解的类
+        provider.addIncludeFilter(new AnnotationTypeFilter(SpringBootTest.class));
+
+        // 接口不会被扫描,其子类会被扫描出来
+        provider.addIncludeFilter(new AssignableTypeFilter(SourceModel.class));
+
+        // Spring会将 .换成/  ("."-based package path to a "/"-based)
+        // Spring拼接的扫描地址:classpath*:xxx/xxx/xxx/**/*.class
+        // Set<BeanDefinition> scanList = provider.findCandidateComponents("com.p7.demo.scanclass");
+        Set<BeanDefinition> scanList = provider.findCandidateComponents("com.elab.spring.*");
+
+        for (BeanDefinition beanDefinition : scanList) {
+            System.out.println(beanDefinition.getBeanClassName());
+        }
+
+    }
+
+    @Test
+    public void patternMatchUtils() {
+        System.out.println(PatternMatchUtils.simpleMatch("liu*", "liukaix"));
+        System.out.println(PatternMatchUtils.simpleMatch("liu*", "li"));
+        System.out.println(PatternMatchUtils.simpleMatch("liu*kai*", "liukaix"));
+        System.out.println(PatternMatchUtils.simpleMatch("liu*x", "liukaix"));
+
+        // 路径匹配
+        match("/api/*/liukx", "/api/xxx/liukx");
+        match("/api/lk?", "/api/lkx");
+        match("/api/lk?", "/api/lkxx");
+        match("/api/xxx/*.html", "/api/xxx/a.html");
+        match("/api/*/aaa", "/api/lkx/aba");
+
+        AntPathMatcher antPathMatcher = new AntPathMatcher();
+
+        Map<String, String> stringStringMap =
+            antPathMatcher.extractUriTemplateVariables("/api/{a}/{b}/{c}", "/api/1/2/3");
+        System.out.println(stringStringMap);
+
+        // 获取通配符匹配的值
+        System.out.println(antPathMatcher.extractPathWithinPattern("/api/*/*.html", "/api/a.html"));
+
+    }
+
+    private AntPathMatcher match(String sourceUrl, String path) {
+        AntPathMatcher antPathMatcher = new AntPathMatcher();
+        System.out.println(sourceUrl + " : " + path + "  > " + antPathMatcher.match(sourceUrl, path));
+        return antPathMatcher;
+    }
+
+}

+ 30 - 0
elab-spring/src/test/java/com/elab/spring/utils/SpringEventUtilsTest.java

@@ -0,0 +1,30 @@
+package com.elab.spring.utils;
+
+import com.elab.spring.config.ThreadConfiguration;
+import com.elab.spring.utils.event.EventA;
+import com.elab.spring.utils.event.EventB;
+import com.elab.spring.utils.event.SourceModel;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = {SpringEventUtils.class, Source3ApplicationListener.class, Source2ApplicationListener.class,
+    ThreadConfiguration.class, SourceApplicationListener.class})
+public class SpringEventUtilsTest {
+
+    @Test
+    public void testSyncEventPublisher() {
+        SourceModel sourceModel = new SourceModel();
+        sourceModel.setName("abc");
+        SpringEventUtils.syncEventPublisher(new EventA(sourceModel));
+
+//        sourceModel.setName("BBBBBBBBBBBBBB");
+//        SpringEventUtils.syncEventPublisher(new EventB(sourceModel));
+    }
+
+    public void testAsyncEventPublisher() {
+
+    }
+}

+ 16 - 0
elab-spring/src/test/java/com/elab/spring/utils/event/EventA.java

@@ -0,0 +1,16 @@
+package com.elab.spring.utils.event;
+
+import org.springframework.context.ApplicationEvent;
+
+public class EventA extends ApplicationEvent {
+
+    /**
+     * Create a new ApplicationEvent.
+     *
+     * @param source the object on which the event initially occurred (never {@code null})
+     */
+    public EventA(SourceModel source) {
+        super(source);
+    }
+
+}

+ 15 - 0
elab-spring/src/test/java/com/elab/spring/utils/event/EventB.java

@@ -0,0 +1,15 @@
+package com.elab.spring.utils.event;
+
+import org.springframework.context.ApplicationEvent;
+
+public class EventB extends ApplicationEvent {
+
+    /**
+     * Create a new ApplicationEvent.
+     *
+     * @param source the object on which the event initially occurred (never {@code null})
+     */
+    public EventB(Object source) {
+        super(source);
+    }
+}

+ 14 - 0
elab-spring/src/test/java/com/elab/spring/utils/event/SourceModel.java

@@ -0,0 +1,14 @@
+package com.elab.spring.utils.event;
+
+public class SourceModel {
+
+    private String name;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+}

+ 33 - 0
elab-test/pom.xml

@@ -0,0 +1,33 @@
+<?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>elab-parent</artifactId>
+        <groupId>com.elab.core</groupId>
+        <version>2.0.5.3-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>elab-test</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.elab.core</groupId>
+            <artifactId>elab-spring</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+</project>

+ 58 - 0
elab-test/src/test/java/com/elab/my/github/FileCase.java

@@ -0,0 +1,58 @@
+package com.elab.my.github;
+
+import org.junit.Test;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.Set;
+
+public class FileCase {
+    Set<String> fileList = new HashSet<>();
+
+    @Test
+    public void testFile() {
+        String filePath = "D:\\github\\MyHome\\文章";
+        File[] files = new File(filePath).listFiles();
+        deepFile(files, 0);
+    }
+
+    private void deepFile(File[] files, int index) {
+        for (int i = 0; i < files.length; i++) {
+            File file = files[i];
+            if (file.isDirectory()) {
+                if (fileList.add(file.getName()) && !isEndWith(file.getName(), "assets")) {
+                    System.out.println("##" + appendIndex("#", index) + " " + file.getName());
+                }
+                deepFile(file.listFiles(), index + 1);
+            } else {
+                if (!isEndWith(file.getName(), "png", "gif", "jpg")) {
+                    System.out.println(
+                        appendIndex("-", index) + "\t" + "[" + file.getName() + "](" + mdPath(file.getPath(),
+                            "D:\\github\\MyHome", ".") + ")");
+                }
+            }
+        }
+    }
+
+    public String mdPath(String path, String replaceStr, String replace) {
+        return path.replace(replaceStr, replace);
+    }
+
+    public String appendIndex(String str, int index) {
+        StringBuilder stringBuilder = new StringBuilder();
+        for (int i = 0; i < index; i++) {
+            stringBuilder.append(str);
+        }
+        return stringBuilder.toString();
+    }
+
+    private boolean isEndWith(String fileName, String... fileEndsWith) {
+        for (int i = 0; i < fileEndsWith.length; i++) {
+            String s = fileEndsWith[i];
+            if (fileName.endsWith(s)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

+ 82 - 0
elab-test/src/test/java/com/elab/my/maven/TestServiceTest.java

@@ -0,0 +1,82 @@
+package com.elab.my.maven;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import junit.framework.TestCase;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+//@SpringBootTest(classes = {TestService.class})
+public class TestServiceTest extends TestCase {
+
+    @Test
+    public void test() {
+        String files =
+            "[ERROR] [FATAL] Non-parseable POM D:\\lib\\maven\\io\\netty\\netty-bom\\4.1.31.Final\\netty-bom-4.1.31.Final.pom: unexpected markup <!d (position: START_DOCUMENT seen \\r\\n<!d... @2:4)  @ D:\\lib\\maven\\io\\netty\\netty-bom\\4.1.31.Final\\netty-bom-4.1.31.Final.pom, line 2, column 4\n"
+                + "[ERROR] [FATAL] Non-parseable POM D:\\lib\\maven\\io\\projectreactor\\reactor-bom\\Californium-SR3\\reactor-bom-Californium-SR3.pom: unexpected markup <!d (position: START_DOCUMENT seen \\r\\n<!d... @2:4)  @ D:\\lib\\maven\\io\\projectreactor\\reactor-bom\\Californium-SR3\\reactor-bom-Californium-SR3.pom, line 2, column 4\n";
+        String[] split = files.split("\n");
+        for (int i = 0; i < split.length; i++) {
+            String s = split[i];
+            int start = s.indexOf("@ ") + 2;
+            int end = s.indexOf(",", start);
+            String file = s.substring(start, end);
+
+            if (file.endsWith("pom")) {
+                System.out.println(file);
+                File f = new File(file);
+                f.delete();
+            }
+        }
+        System.out.println(split);
+    }
+
+    @Test
+    public void testDPom() throws Exception {
+        String pom =
+            "[WARNING] The POM for org.springframework.cloud:spring-cloud-starter-config:jar:2.0.0.RELEASE is invalid, transitive dependencies (if any) will not be available, enable debug logging for more details\n"
+                + "[WARNING] The POM for org.springframework.boot:spring-boot-starter-web:jar:2.0.0.RELEASE is invalid, transitive dependencies (if any) will not be available, enable debug logging for more details\n"
+                + "[WARNING] The POM for org.springframework.boot:spring-boot-starter-test:jar:2.0.0.RELEASE is invalid, transitive dependencies (if any) will not be available, enable debug logging for more details\n"
+                + "[WARNING] The POM for org.springframework.cloud:spring-cloud-config-server:jar:2.0.0.RELEASE is invalid, transitive dependencies (if any) will not be available, enable debug logging for more details\n";
+        String[] split = pom.split("\n");
+        for (int i = 0; i < split.length; i++) {
+            String s = split[i];
+            int start = s.indexOf("for ") + 4;
+            int end = s.indexOf(" is ", start);
+            String file = s.substring(start, end);
+            StringBuilder sb = new StringBuilder("D:\\lib\\maven\\");
+            String[] fileList = file.split(":");
+            sb.append(fileList[0].replaceAll("\\.", "\\\\"));
+            sb.append("\\" + fileList[1]);
+            sb.append("\\" + fileList[3]);
+
+            File f = new File(sb.toString());
+            deleteFile(f);
+            f.delete();
+            System.out.println(sb.toString() + " : \t " + f.exists());
+        }
+    }
+
+    private static void deleteFile(File file) throws Exception {
+        /**
+         * File[] listFiles()
+         *  返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。
+         */
+        File[] files = file.listFiles();
+        if (files != null) {//如果包含文件进行删除操作
+            for (int i = 0; i < files.length; i++) {
+                if (files[i].isFile()) {
+                    //删除子文件
+                    files[i].delete();
+                } else if (files[i].isDirectory()) {
+                    //通过递归的方法找到子目录的文件
+                    deleteFile(files[i]);
+                }
+                files[i].delete();//删除子目录
+            }
+        }
+    }
+}

+ 1 - 0
pom.xml

@@ -23,6 +23,7 @@
         <module>elab-es</module>
         <module>elab-redis</module>
         <module>elab-kafka</module>
+        <module>elab-test</module>
     </modules>