home 2 лет назад
Сommit
7df7684f79
100 измененных файлов с 8612 добавлено и 0 удалено
  1. 579 0
      bladex-tool/blade-bom/pom.xml
  2. 38 0
      bladex-tool/blade-core-auto/pom.xml
  3. 35 0
      bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/annotation/AutoContextInitializer.java
  4. 35 0
      bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/annotation/AutoFailureAnalyzer.java
  5. 35 0
      bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/annotation/AutoIgnore.java
  6. 35 0
      bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/annotation/AutoListener.java
  7. 35 0
      bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/annotation/AutoRunListener.java
  8. 81 0
      bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/common/AbstractBladeProcessor.java
  9. 55 0
      bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/common/BootAutoType.java
  10. 132 0
      bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/common/MultiSetMap.java
  11. 43 0
      bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/common/Sets.java
  12. 123 0
      bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/common/TypeHelper.java
  13. 199 0
      bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/factories/AutoFactoriesProcessor.java
  14. 80 0
      bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/factories/FactoriesFiles.java
  15. 47 0
      bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/service/AutoService.java
  16. 254 0
      bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/service/AutoServiceProcessor.java
  17. 77 0
      bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/service/ServicesFiles.java
  18. 2 0
      bladex-tool/blade-core-auto/src/main/resources/META-INF/services/javax.annotation.processing.Processor
  19. 60 0
      bladex-tool/blade-core-boot/pom.xml
  20. 37 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/config/BladeBootAutoConfiguration.java
  21. 132 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/config/BladeExecutorConfiguration.java
  22. 49 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/config/BladeRetryConfiguration.java
  23. 62 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/config/BladeWebMvcConfiguration.java
  24. 57 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/config/RequestConfiguration.java
  25. 279 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/ctrl/BladeController.java
  26. 57 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/error/ErrorType.java
  27. 53 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/error/ErrorUtil.java
  28. 240 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/file/BladeFileUtil.java
  29. 51 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/file/FileProxyManager.java
  30. 53 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/file/IFileProxy.java
  31. 157 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/file/LocalFile.java
  32. 117 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/file/LocalFileProxyFactory.java
  33. 76 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/props/BladeFileProperties.java
  34. 43 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/props/BladeUploadProperties.java
  35. 120 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/request/BladeHttpServletRequestWrapper.java
  36. 75 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/request/BladeRequestFilter.java
  37. 44 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/request/RequestProperties.java
  38. 536 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/request/XssHtmlFilter.java
  39. 175 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/request/XssHttpServletRequestWrapper.java
  40. 44 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/request/XssProperties.java
  41. 64 0
      bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/resolver/TokenArgumentResolver.java
  42. 8 0
      bladex-tool/blade-core-boot/src/main/resources/banner.txt
  43. 31 0
      bladex-tool/blade-core-boot/src/main/resources/blade-boot.yml
  44. BIN
      bladex-tool/blade-core-boot/src/main/resources/static/favicon.ico
  45. 102 0
      bladex-tool/blade-core-cloud/pom.xml
  46. 39 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/annotation/ApiVersion.java
  47. 38 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/annotation/UrlVersion.java
  48. 107 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/annotation/VersionMapping.java
  49. 40 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/client/BladeCloudApplication.java
  50. 45 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/feign/BladeFallbackFactory.java
  51. 102 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/feign/BladeFeignFallback.java
  52. 48 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/feign/BladeFeignRequestInterceptor.java
  53. 81 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/feign/EnableBladeFeign.java
  54. 30 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/BladeHttpConfiguration.java
  55. 65 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/BladeHttpProperties.java
  56. 27 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/LbRestTemplate.java
  57. 191 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/RestTemplateConfiguration.java
  58. 48 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/RestTemplateHeaderInterceptor.java
  59. 257 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/logger/HttpLoggingInterceptor.java
  60. 33 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/logger/OkHttpSlf4jLogger.java
  61. 126 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/hystrix/BladeHystrixAutoConfiguration.java
  62. 91 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/hystrix/BladeHystrixConcurrencyStrategy.java
  63. 26 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeBlockExceptionHandler.java
  64. 128 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeFeignSentinel.java
  65. 64 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeSentinelAutoConfiguration.java
  66. 169 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeSentinelInvocationHandler.java
  67. 46 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/server/UndertowHttp2Configuration.java
  68. 49 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/version/BladeMediaType.java
  69. 104 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/version/BladeRequestMappingHandlerMapping.java
  70. 101 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/version/BladeSpringMvcContract.java
  71. 44 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/version/BladeWebMvcRegistrations.java
  72. 38 0
      bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/version/VersionMappingAutoConfiguration.java
  73. 229 0
      bladex-tool/blade-core-cloud/src/main/java/org/springframework/cloud/openfeign/BladeFeignClientsRegistrar.java
  74. 99 0
      bladex-tool/blade-core-cloud/src/main/java/org/springframework/cloud/openfeign/BladeHystrixTargeter.java
  75. 39 0
      bladex-tool/blade-core-cloud/src/main/java/org/springframework/cloud/openfeign/Targeter.java
  76. 31 0
      bladex-tool/blade-core-context/pom.xml
  77. 65 0
      bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/BladeCallableWrapper.java
  78. 73 0
      bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/BladeContext.java
  79. 50 0
      bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/BladeHttpHeadersGetter.java
  80. 64 0
      bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/BladeRunnableWrapper.java
  81. 80 0
      bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/BladeServletContext.java
  82. 75 0
      bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/ServletHttpHeadersGetter.java
  83. 53 0
      bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/config/BladeContextAutoConfiguration.java
  84. 42 0
      bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/config/BladeServletListenerConfiguration.java
  85. 74 0
      bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/listener/BladeServletRequestListener.java
  86. 81 0
      bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/props/BladeContextProperties.java
  87. 72 0
      bladex-tool/blade-core-db/pom.xml
  88. 31 0
      bladex-tool/blade-core-db/src/main/java/org/springblade/core/db/config/DbConfiguration.java
  89. 6 0
      bladex-tool/blade-core-db/src/main/java/org/springblade/core/db/package-info.java
  90. 39 0
      bladex-tool/blade-core-db/src/main/resources/blade-db.yml
  91. 42 0
      bladex-tool/blade-core-launch/pom.xml
  92. 129 0
      bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/BladeApplication.java
  93. 47 0
      bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/StartEventListener.java
  94. 34 0
      bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/config/BladeLaunchConfiguration.java
  95. 43 0
      bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/config/BladePropertyConfiguration.java
  96. 131 0
      bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/constant/AppConstant.java
  97. 50 0
      bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/constant/ConsulConstant.java
  98. 31 0
      bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/constant/FlowConstant.java
  99. 128 0
      bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/constant/NacosConstant.java
  100. 30 0
      bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/constant/SentinelConstant.java

+ 579 - 0
bladex-tool/blade-bom/pom.xml

@@ -0,0 +1,579 @@
+<?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">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.3.12.RELEASE</version>
+        <relativePath />
+    </parent>
+
+    <groupId>org.springblade.platform</groupId>
+    <artifactId>blade-bom</artifactId>
+    <packaging>pom</packaging>
+    <version>${bladex.tool.version}</version>
+    <description>bladex统一版本配置</description>
+
+    <properties>
+        <bladex.tool.version>2.8.2.RELEASE</bladex.tool.version>
+
+        <swagger.version>2.10.5</swagger.version>
+        <swagger.models.version>1.6.2</swagger.models.version>
+        <knife4j.version>2.0.9</knife4j.version>
+        <mybatis.plus.version>3.4.2</mybatis.plus.version>
+        <mybatis.plus.generator.version>3.4.1</mybatis.plus.generator.version>
+        <mybatis.plus.dynamic.version>3.3.6</mybatis.plus.dynamic.version>
+        <protostuff.version>1.6.0</protostuff.version>
+        <disruptor.version>3.4.2</disruptor.version>
+        <logstash.version>6.2</logstash.version>
+        <druid.version>1.2.5</druid.version>
+        <jackson.version>2.11.4</jackson.version>
+        <okhttp.version>3.14.5</okhttp.version>
+        <xxl.job.version>2.1.2</xxl.job.version>
+
+        <mysql.connector.version>8.0.22</mysql.connector.version>
+        <oracle.connector.version>12.2.0.1</oracle.connector.version>
+        <postgresql.connector.version>42.2.22</postgresql.connector.version>
+        <sqlserver.connector.version>8.4.1.jre8</sqlserver.connector.version>
+
+        <spring.boot.admin.version>2.3.1</spring.boot.admin.version>
+        <alibaba.cloud.version>2.2.5.RELEASE</alibaba.cloud.version>
+        <alibaba.seata.version>1.4.2</alibaba.seata.version>
+        <alibaba.nacos.version>2.0.2</alibaba.nacos.version>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <!-- Blade -->
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-core-auto</artifactId>
+                <version>${bladex.tool.version}</version>
+                <scope>provided</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-core-boot</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-core-cloud</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-core-context</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-core-db</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-core-launch</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-core-log4j2</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-core-secure</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-core-test</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-core-tool</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-actuate</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-api-crypto</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-auth</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-cache</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-datascope</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-develop</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-ehcache</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-excel</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-http</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-jwt</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-log</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-metrics</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-mongo</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-mybatis</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-oss</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-prometheus</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-redis</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-report</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-ribbon</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-sms</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-social</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-swagger</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-tenant</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-trace</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springblade</groupId>
+                <artifactId>blade-starter-transaction</artifactId>
+                <version>${bladex.tool.version}</version>
+            </dependency>
+            <!-- Nacos -->
+            <dependency>
+                <groupId>com.alibaba.cloud</groupId>
+                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+                <version>${alibaba.cloud.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.cloud</groupId>
+                <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+                <version>${alibaba.cloud.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba.nacos</groupId>
+                <artifactId>nacos-client</artifactId>
+                <version>${alibaba.nacos.version}</version>
+            </dependency>
+            <!-- Sentinel -->
+            <dependency>
+                <groupId>com.alibaba.cloud</groupId>
+                <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
+                <version>${alibaba.cloud.version}</version>
+            </dependency>
+            <!-- Seata-->
+            <dependency>
+                <groupId>com.alibaba.cloud</groupId>
+                <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
+                <version>${alibaba.cloud.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>io.seata</groupId>
+                <artifactId>seata-spring-boot-starter</artifactId>
+                <version>${alibaba.seata.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>io.seata</groupId>
+                <artifactId>seata-all</artifactId>
+                <version>${alibaba.seata.version}</version>
+            </dependency>
+            <!--Mybatis-->
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus</artifactId>
+                <version>${mybatis.plus.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-boot-starter</artifactId>
+                <version>${mybatis.plus.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-extension</artifactId>
+                <version>${mybatis.plus.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>mybatis-plus-generator</artifactId>
+                <version>${mybatis.plus.generator.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.baomidou</groupId>
+                <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
+                <version>${mybatis.plus.dynamic.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.mybatis</groupId>
+                <artifactId>mybatis-spring</artifactId>
+                <version>2.0.6</version>
+            </dependency>
+            <dependency>
+                <groupId>org.mybatis</groupId>
+                <artifactId>mybatis</artifactId>
+                <version>3.5.6</version>
+            </dependency>
+            <dependency>
+                <groupId>org.mybatis</groupId>
+                <artifactId>mybatis-typehandlers-jsr310</artifactId>
+                <version>1.0.2</version>
+            </dependency>
+            <!-- JWT -->
+            <dependency>
+                <groupId>io.jsonwebtoken</groupId>
+                <artifactId>jjwt-impl</artifactId>
+                <version>0.11.2</version>
+            </dependency>
+            <dependency>
+                <groupId>io.jsonwebtoken</groupId>
+                <artifactId>jjwt-jackson</artifactId>
+                <version>0.11.2</version>
+            </dependency>
+            <!-- Admin -->
+            <dependency>
+                <groupId>de.codecentric</groupId>
+                <artifactId>spring-boot-admin-starter-server</artifactId>
+                <version>${spring.boot.admin.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>de.codecentric</groupId>
+                <artifactId>spring-boot-admin-starter-client</artifactId>
+                <version>${spring.boot.admin.version}</version>
+            </dependency>
+            <!-- Druid -->
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>druid</artifactId>
+                <version>${druid.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>druid-spring-boot-starter</artifactId>
+                <version>${druid.version}</version>
+            </dependency>
+            <!-- MySql -->
+            <dependency>
+                <groupId>mysql</groupId>
+                <artifactId>mysql-connector-java</artifactId>
+                <version>${mysql.connector.version}</version>
+            </dependency>
+            <!-- Oracle -->
+            <dependency>
+                <groupId>com.oracle</groupId>
+                <artifactId>ojdbc7</artifactId>
+                <version>${oracle.connector.version}</version>
+            </dependency>
+            <!-- PostgreSql -->
+            <dependency>
+                <groupId>org.postgresql</groupId>
+                <artifactId>postgresql</artifactId>
+                <version>${postgresql.connector.version}</version>
+            </dependency>
+            <!-- SqlServer -->
+            <dependency>
+                <groupId>com.microsoft.sqlserver</groupId>
+                <artifactId>mssql-jdbc</artifactId>
+                <version>${sqlserver.connector.version}</version>
+            </dependency>
+            <!--Swagger-->
+            <dependency>
+                <groupId>io.springfox</groupId>
+                <artifactId>springfox-swagger2</artifactId>
+                <version>${swagger.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>io.swagger</groupId>
+                <artifactId>swagger-models</artifactId>
+                <version>${swagger.models.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.github.xiaoymin</groupId>
+                <artifactId>knife4j-micro-spring-boot-starter</artifactId>
+                <version>${knife4j.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.github.xiaoymin</groupId>
+                <artifactId>knife4j-aggregation-spring-boot-starter</artifactId>
+                <version>${knife4j.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.github.xiaoymin</groupId>
+                <artifactId>knife4j-spring-ui</artifactId>
+                <version>${knife4j.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework.plugin</groupId>
+                <artifactId>spring-plugin-core</artifactId>
+                <version>2.0.0.RELEASE</version>
+            </dependency>
+            <dependency>
+                <groupId>org.springframework.plugin</groupId>
+                <artifactId>spring-plugin-metadata</artifactId>
+                <version>2.0.0.RELEASE</version>
+            </dependency>
+            <!-- protostuff -->
+            <dependency>
+                <groupId>io.protostuff</groupId>
+                <artifactId>protostuff-core</artifactId>
+                <version>${protostuff.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>io.protostuff</groupId>
+                <artifactId>protostuff-runtime</artifactId>
+                <version>${protostuff.version}</version>
+            </dependency>
+            <!-- http -->
+            <dependency>
+                <groupId>org.jsoup</groupId>
+                <artifactId>jsoup</artifactId>
+                <version>1.12.1</version>
+            </dependency>
+            <dependency>
+                <groupId>com.squareup.okhttp3</groupId>
+                <artifactId>okhttp</artifactId>
+                <version>${okhttp.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>com.squareup.okhttp3</groupId>
+                <artifactId>logging-interceptor</artifactId>
+                <version>${okhttp.version}</version>
+            </dependency>
+            <!-- redisson -->
+            <dependency>
+                <groupId>org.redisson</groupId>
+                <artifactId>redisson</artifactId>
+                <version>3.11.6</version>
+            </dependency>
+            <!-- Disruptor -->
+            <dependency>
+                <groupId>com.lmax</groupId>
+                <artifactId>disruptor</artifactId>
+                <version>${disruptor.version}</version>
+            </dependency>
+            <!-- Logstash -->
+            <dependency>
+                <groupId>net.logstash.logback</groupId>
+                <artifactId>logstash-logback-encoder</artifactId>
+                <version>${logstash.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.codehaus.janino</groupId>
+                <artifactId>janino</artifactId>
+                <version>3.0.15</version>
+            </dependency>
+            <!-- xxljob -->
+            <dependency>
+                <groupId>com.xuxueli</groupId>
+                <artifactId>xxl-job-core</artifactId>
+                <version>${xxl.job.version}</version>
+            </dependency>
+            <!-- captcha -->
+            <dependency>
+                <groupId>com.github.whvcse</groupId>
+                <artifactId>easy-captcha</artifactId>
+                <version>1.6.2</version>
+            </dependency>
+            <!-- jackson -->
+            <dependency>
+                <groupId>com.fasterxml.jackson.module</groupId>
+                <artifactId>jackson-module-jaxb-annotations</artifactId>
+                <version>${jackson.version}</version>
+            </dependency>
+            <!-- fastjson -->
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>fastjson</artifactId>
+                <version>1.2.76</version>
+            </dependency>
+            <!-- sentinel -->
+            <dependency>
+                <groupId>com.alibaba.csp</groupId>
+                <artifactId>sentinel-core</artifactId>
+                <version>1.8.0</version>
+            </dependency>
+            <!-- easyexcel -->
+            <dependency>
+                <groupId>com.alibaba</groupId>
+                <artifactId>easyexcel</artifactId>
+                <version>2.2.10</version>
+            </dependency>
+            <!-- JustAuth -->
+            <dependency>
+                <groupId>me.zhyd.oauth</groupId>
+                <artifactId>JustAuth</artifactId>
+                <version>1.16.1</version>
+            </dependency>
+            <!-- Guava -->
+            <dependency>
+                <groupId>com.google.guava</groupId>
+                <artifactId>guava</artifactId>
+                <version>30.1.1-jre</version>
+            </dependency>
+            <!-- Prometheus -->
+            <dependency>
+                <groupId>io.micrometer</groupId>
+                <artifactId>micrometer-core</artifactId>
+                <version>1.6.3</version>
+            </dependency>
+            <dependency>
+                <groupId>io.micrometer</groupId>
+                <artifactId>micrometer-registry-prometheus</artifactId>
+                <version>1.6.3</version>
+            </dependency>
+            <!--AliOss-->
+            <dependency>
+                <groupId>com.aliyun.oss</groupId>
+                <artifactId>aliyun-sdk-oss</artifactId>
+                <version>3.11.3</version>
+            </dependency>
+            <!--MinIO-->
+            <dependency>
+                <groupId>io.minio</groupId>
+                <artifactId>minio</artifactId>
+                <version>8.1.0</version>
+            </dependency>
+            <!--QiNiu-->
+            <dependency>
+                <groupId>com.qiniu</groupId>
+                <artifactId>qiniu-java-sdk</artifactId>
+                <version>7.4.0</version>
+            </dependency>
+            <!--腾讯COS-->
+            <dependency>
+                <groupId>com.qcloud</groupId>
+                <artifactId>cos_api</artifactId>
+                <version>5.6.36</version>
+            </dependency>
+            <!--华为云Obs-->
+            <dependency>
+                <groupId>com.huaweicloud</groupId>
+                <artifactId>esdk-obs-java</artifactId>
+                <version>3.19.7</version>
+            </dependency>
+            <!--AliSms-->
+            <dependency>
+                <groupId>com.aliyun</groupId>
+                <artifactId>aliyun-java-sdk-core</artifactId>
+                <version>4.5.18</version>
+            </dependency>
+            <!--YunPian-->
+            <dependency>
+                <groupId>com.yunpian.sdk</groupId>
+                <artifactId>yunpian-java-sdk</artifactId>
+                <version>1.2.7</version>
+            </dependency>
+            <!--腾讯SMS-->
+            <dependency>
+                <groupId>com.github.qcloudsms</groupId>
+                <artifactId>qcloudsms</artifactId>
+                <version>1.0.6</version>
+            </dependency>
+            <!-- findbugs -->
+            <dependency>
+                <groupId>com.google.code.findbugs</groupId>
+                <artifactId>jsr305</artifactId>
+                <version>3.0.2</version>
+            </dependency>
+            <dependency>
+                <groupId>org.projectlombok</groupId>
+                <artifactId>lombok</artifactId>
+                <version>1.18.18</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <distributionManagement>
+        <repository>
+            <id>blade-release</id>
+            <name>Release Repository</name>
+            <url>http://nexus.bladex.vip/repository/maven-releases/</url>
+        </repository>
+    </distributionManagement>
+
+</project>

+ 38 - 0
bladex-tool/blade-core-auto/pom.xml

@@ -0,0 +1,38 @@
+<?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>BladeX-Tool</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.8.2.RELEASE</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-core-auto</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${project.parent.version}</version>
+    <packaging>jar</packaging>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>${maven.plugin.version}</version>
+                <configuration>
+                    <annotationProcessorPaths>
+                        <path>
+                            <groupId>org.hibernate.validator</groupId>
+                            <artifactId>hibernate-validator-annotation-processor</artifactId>
+                            <version>6.0.13.Final</version>
+                        </path>
+                    </annotationProcessorPaths>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 35 - 0
bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/annotation/AutoContextInitializer.java

@@ -0,0 +1,35 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.auto.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * ApplicationContextInitializer 处理
+ *
+ * @author L.cm
+ */
+@Documented
+@Retention(SOURCE)
+@Target(TYPE)
+public @interface AutoContextInitializer {
+}

+ 35 - 0
bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/annotation/AutoFailureAnalyzer.java

@@ -0,0 +1,35 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.auto.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * FailureAnalyzer 处理
+ *
+ * @author L.cm
+ */
+@Documented
+@Retention(SOURCE)
+@Target(TYPE)
+public @interface AutoFailureAnalyzer {
+}

+ 35 - 0
bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/annotation/AutoIgnore.java

@@ -0,0 +1,35 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.auto.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * AutoIgnore 处理
+ *
+ * @author L.cm
+ */
+@Documented
+@Retention(SOURCE)
+@Target(TYPE)
+public @interface AutoIgnore {
+}

+ 35 - 0
bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/annotation/AutoListener.java

@@ -0,0 +1,35 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.auto.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * ApplicationListener 处理
+ *
+ * @author L.cm
+ */
+@Documented
+@Retention(SOURCE)
+@Target(TYPE)
+public @interface AutoListener {
+}

+ 35 - 0
bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/annotation/AutoRunListener.java

@@ -0,0 +1,35 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.auto.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+/**
+ * SpringApplicationRunListener 处理
+ *
+ * @author L.cm
+ */
+@Documented
+@Retention(SOURCE)
+@Target(TYPE)
+public @interface AutoRunListener {
+}

+ 81 - 0
bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/common/AbstractBladeProcessor.java

@@ -0,0 +1,81 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.auto.common;
+
+import javax.annotation.processing.AbstractProcessor;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic.Kind;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Set;
+
+/**
+ * 抽象 处理器
+ *
+ * @author L.cm
+ */
+public abstract class AbstractBladeProcessor extends AbstractProcessor {
+
+	@Override
+	public SourceVersion getSupportedSourceVersion() {
+		return SourceVersion.latestSupported();
+	}
+
+	/**
+	 * AutoService 注解处理器
+	 * @param annotations 注解 getSupportedAnnotationTypes
+	 * @param roundEnv 扫描到的 注解新
+	 * @return 是否完成
+	 */
+	@Override
+	public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+		try {
+			return processImpl(annotations, roundEnv);
+		} catch (Exception e) {
+			fatalError(e);
+			return false;
+		}
+	}
+
+	protected abstract boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv);
+
+	protected void log(String msg) {
+		if (processingEnv.getOptions().containsKey("debug")) {
+			processingEnv.getMessager().printMessage(Kind.NOTE, msg);
+		}
+	}
+
+	protected void error(String msg, Element element, AnnotationMirror annotation) {
+		processingEnv.getMessager().printMessage(Kind.ERROR, msg, element, annotation);
+	}
+
+	protected void fatalError(Exception e) {
+		// We don't allow exceptions of any kind to propagate to the compiler
+		StringWriter writer = new StringWriter();
+		e.printStackTrace(new PrintWriter(writer));
+		fatalError(writer.toString());
+	}
+
+	protected void fatalError(String msg) {
+		processingEnv.getMessager().printMessage(Kind.ERROR, "FATAL ERROR: " + msg);
+	}
+
+}

+ 55 - 0
bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/common/BootAutoType.java

@@ -0,0 +1,55 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.auto.common;
+
+import org.springblade.core.auto.annotation.AutoContextInitializer;
+import org.springblade.core.auto.annotation.AutoFailureAnalyzer;
+import org.springblade.core.auto.annotation.AutoListener;
+import org.springblade.core.auto.annotation.AutoRunListener;
+
+/**
+ * 注解类型
+ *
+ * @author L.cm
+ */
+public enum BootAutoType {
+	/**
+	 * 注解处理的类型
+	 */
+	CONTEXT_INITIALIZER(AutoContextInitializer.class.getName(), "org.springframework.context.ApplicationContextInitializer"),
+	LISTENER(AutoListener.class.getName(), "org.springframework.context.ApplicationListener"),
+	RUN_LISTENER(AutoRunListener.class.getName(), "org.springframework.boot.SpringApplicationRunListener"),
+	FAILURE_ANALYZER(AutoFailureAnalyzer.class.getName(), "org.springframework.boot.diagnostics.FailureAnalyzer"),
+	COMPONENT("org.springframework.stereotype.Component", "org.springframework.boot.autoconfigure.EnableAutoConfiguration");
+
+	private final String annotationName;
+	private final String configureKey;
+
+	BootAutoType(String annotationName, String configureKey) {
+		this.annotationName = annotationName;
+		this.configureKey = configureKey;
+	}
+
+	public final String getAnnotationName() {
+		return annotationName;
+	}
+
+	public final String getConfigureKey() {
+		return configureKey;
+	}
+
+}

+ 132 - 0
bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/common/MultiSetMap.java

@@ -0,0 +1,132 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.auto.common;
+
+import java.util.*;
+
+/**
+ * MultiSetMap
+ *
+ * @author L.cm
+ */
+public class MultiSetMap<K, V> {
+	private transient final Map<K, Set<V>> map;
+
+	public MultiSetMap() {
+		map = new HashMap<>();
+	}
+
+	private Set<V> createSet() {
+		return new HashSet<>();
+	}
+
+	/**
+	 * put to MultiSetMap
+	 *
+	 * @param key   键
+	 * @param value 值
+	 * @return boolean
+	 */
+	public boolean put(K key, V value) {
+		Set<V> set = map.get(key);
+		if (set == null) {
+			set = createSet();
+			if (set.add(value)) {
+				map.put(key, set);
+				return true;
+			} else {
+				throw new AssertionError("New set violated the set spec");
+			}
+		} else if (set.add(value)) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * 是否包含某个key
+	 *
+	 * @param key key
+	 * @return 结果
+	 */
+	public boolean containsKey(K key) {
+		return map.containsKey(key);
+	}
+
+	/**
+	 * 是否包含 value 中的某个值
+	 *
+	 * @param value value
+	 * @return 是否包含
+	 */
+	public boolean containsVal(V value) {
+		Collection<Set<V>> values = map.values();
+		return values.stream().anyMatch(vs -> vs.contains(value));
+	}
+
+	/**
+	 * key 集合
+	 *
+	 * @return keys
+	 */
+	public Set<K> keySet() {
+		return map.keySet();
+	}
+
+	/**
+	 * put list to MultiSetMap
+	 *
+	 * @param key 键
+	 * @param set 值列表
+	 * @return boolean
+	 */
+	public boolean putAll(K key, Set<V> set) {
+		if (set == null) {
+			return false;
+		} else {
+			map.put(key, set);
+			return true;
+		}
+	}
+
+	/**
+	 * get List by key
+	 *
+	 * @param key 键
+	 * @return List
+	 */
+	public Set<V> get(K key) {
+		return map.get(key);
+	}
+
+	/**
+	 * clear MultiSetMap
+	 */
+	public void clear() {
+		map.clear();
+	}
+
+	/**
+	 * isEmpty
+	 *
+	 * @return isEmpty
+	 */
+	public boolean isEmpty() {
+		return map.isEmpty();
+	}
+}

+ 43 - 0
bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/common/Sets.java

@@ -0,0 +1,43 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.auto.common;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * 集合 工具类
+ *
+ * @author L.cm
+ */
+public class Sets {
+
+	/**
+	 * 不可变 集合
+	 *
+	 * @param es  对象
+	 * @param <E> 泛型
+	 * @return 集合
+	 */
+	@SafeVarargs
+	public static <E> Set<E> ofImmutableSet(E... es) {
+		Objects.requireNonNull(es);
+		return Stream.of(es).collect(Collectors.toSet());
+	}
+}

+ 123 - 0
bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/common/TypeHelper.java

@@ -0,0 +1,123 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.auto.common;
+
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.QualifiedNameable;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Types;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Type utilities.
+ *
+ * @author Stephane Nicoll
+ * @since 5.0
+ */
+public class TypeHelper {
+
+	private final ProcessingEnvironment env;
+
+	private final Types types;
+
+
+	public TypeHelper(ProcessingEnvironment env) {
+		this.env = env;
+		this.types = env.getTypeUtils();
+	}
+
+
+	public String getType(Element element) {
+		return getType(element != null ? element.asType() : null);
+	}
+
+	public String getType(AnnotationMirror annotation) {
+		return getType(annotation != null ? annotation.getAnnotationType() : null);
+	}
+
+	public String getType(TypeMirror type) {
+		if (type == null) {
+			return null;
+		}
+		if (type instanceof DeclaredType) {
+			DeclaredType declaredType = (DeclaredType) type;
+			Element enclosingElement = declaredType.asElement().getEnclosingElement();
+			if (enclosingElement != null && enclosingElement instanceof TypeElement) {
+				return getQualifiedName(enclosingElement) + "$" + declaredType.asElement().getSimpleName().toString();
+			} else {
+				return getQualifiedName(declaredType.asElement());
+			}
+		}
+		return type.toString();
+	}
+
+	private String getQualifiedName(Element element) {
+		if (element instanceof QualifiedNameable) {
+			return ((QualifiedNameable) element).getQualifiedName().toString();
+		}
+		return element.toString();
+	}
+
+	/**
+	 * Return the super class of the specified {@link Element} or null if this
+	 * {@code element} represents {@link Object}.
+	 *
+	 * @param element Element
+	 * @return Element
+	 */
+	public Element getSuperClass(Element element) {
+		List<? extends TypeMirror> superTypes = this.types.directSupertypes(element.asType());
+		if (superTypes.isEmpty()) {
+			// reached java.lang.Object
+			return null;
+		}
+		return this.types.asElement(superTypes.get(0));
+	}
+
+	/**
+	 * Return the interfaces that are <strong>directly</strong> implemented by the
+	 * specified {@link Element} or an empty list if this {@code element} does not
+	 * implement any interface.
+	 *
+	 * @param element Element
+	 * @return Element list
+	 */
+	public List<Element> getDirectInterfaces(Element element) {
+		List<? extends TypeMirror> superTypes = this.types.directSupertypes(element.asType());
+		List<Element> directInterfaces = new ArrayList<>();
+		// index 0 is the super class
+		if (superTypes.size() > 1) {
+			for (int i = 1; i < superTypes.size(); i++) {
+				Element e = this.types.asElement(superTypes.get(i));
+				if (e != null) {
+					directInterfaces.add(e);
+				}
+			}
+		}
+		return directInterfaces;
+	}
+
+	public List<? extends AnnotationMirror> getAllAnnotationMirrors(Element e) {
+		return this.env.getElementUtils().getAllAnnotationMirrors(e);
+	}
+
+}

+ 199 - 0
bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/factories/AutoFactoriesProcessor.java

@@ -0,0 +1,199 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.auto.factories;
+
+import org.springblade.core.auto.annotation.AutoIgnore;
+import org.springblade.core.auto.common.AbstractBladeProcessor;
+import org.springblade.core.auto.common.BootAutoType;
+import org.springblade.core.auto.common.MultiSetMap;
+import org.springblade.core.auto.service.AutoService;
+
+import javax.annotation.processing.*;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.util.Elements;
+import javax.tools.FileObject;
+import javax.tools.StandardLocation;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * spring boot 自动配置处理器
+ *
+ * @author L.cm
+ */
+@AutoService(Processor.class)
+@SupportedAnnotationTypes("*")
+@SupportedOptions("debug")
+public class AutoFactoriesProcessor extends AbstractBladeProcessor {
+	/**
+	 * 处理的注解 @FeignClient
+	 */
+	private static final String FEIGN_CLIENT_ANNOTATION = "org.springframework.cloud.openfeign.FeignClient";
+	/**
+	 * Feign 自动配置
+	 */
+	private static final String FEIGN_AUTO_CONFIGURE_KEY = "org.springblade.core.cloud.feign.BladeFeignAutoConfiguration";
+	/**
+	 * The location to look for factories.
+	 * <p>Can be present in multiple JAR files.
+	 */
+	private static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
+	/**
+	 * devtools,有 Configuration 注解的 jar 一般需要 devtools 配置文件
+	 */
+	private static final String DEVTOOLS_RESOURCE_LOCATION = "META-INF/spring-devtools.properties";
+	/**
+	 * 数据承载
+	 */
+	private final MultiSetMap<String, String> factories = new MultiSetMap<>();
+	/**
+	 * 元素辅助类
+	 */
+	private Elements elementUtils;
+
+	@Override
+	public synchronized void init(ProcessingEnvironment processingEnv) {
+		super.init(processingEnv);
+		elementUtils = processingEnv.getElementUtils();
+	}
+
+	@Override
+	protected boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+		if (roundEnv.processingOver()) {
+			generateFactoriesFiles();
+		} else {
+			processAnnotations(annotations, roundEnv);
+		}
+		return false;
+	}
+
+	private void processAnnotations(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+		// 日志 打印信息 gradle build --debug
+		log(annotations.toString());
+		Set<? extends Element> elementSet = roundEnv.getRootElements();
+		log("All Element set: " + elementSet.toString());
+
+		// 过滤 TypeElement
+		Set<TypeElement> typeElementSet = elementSet.stream()
+			.filter(this::isClassOrInterface)
+			.filter(e -> e instanceof TypeElement)
+			.map(e -> (TypeElement) e)
+			.collect(Collectors.toSet());
+		// 如果为空直接跳出
+		if (typeElementSet.isEmpty()) {
+			log("Annotations elementSet is isEmpty");
+			return;
+		}
+
+		for (TypeElement typeElement : typeElementSet) {
+			if (isAnnotation(elementUtils, typeElement, AutoIgnore.class.getName())) {
+				log("Found @AutoIgnore annotation,ignore Element: " + typeElement.toString());
+			} else if (isAnnotation(elementUtils, typeElement, FEIGN_CLIENT_ANNOTATION)) {
+				log("Found @FeignClient Element: " + typeElement.toString());
+
+				ElementKind elementKind = typeElement.getKind();
+				// Feign Client 只处理 接口
+				if (ElementKind.INTERFACE != elementKind) {
+					fatalError("@FeignClient Element " + typeElement.toString() + " 不是接口。");
+					continue;
+				}
+
+				String factoryName = typeElement.getQualifiedName().toString();
+				if (factories.containsVal(factoryName)) {
+					continue;
+				}
+
+				log("读取到新配置 spring.factories factoryName:" + factoryName);
+				factories.put(FEIGN_AUTO_CONFIGURE_KEY, factoryName);
+			} else {
+				for (BootAutoType autoType : BootAutoType.values()) {
+					String annotation = autoType.getAnnotationName();
+					if (isAnnotation(elementUtils, typeElement, annotation)) {
+						log("Found @" + annotation + " Element: " + typeElement.toString());
+
+						String factoryName = typeElement.getQualifiedName().toString();
+						if (factories.containsVal(factoryName)) {
+							continue;
+						}
+
+						log("读取到新配置 spring.factories factoryName:" + factoryName);
+						factories.put(autoType.getConfigureKey(), factoryName);
+					}
+				}
+			}
+		}
+	}
+
+	private void generateFactoriesFiles() {
+		if (factories.isEmpty()) {
+			return;
+		}
+		Filer filer = processingEnv.getFiler();
+		try {
+			// 1. spring.factories
+			FileObject factoriesFile = filer.createResource(StandardLocation.CLASS_OUTPUT, "", FACTORIES_RESOURCE_LOCATION);
+			FactoriesFiles.writeFactoriesFile(factories, factoriesFile.openOutputStream());
+			String classesPath = factoriesFile.toUri().toString().split("classes")[0];
+			Path projectPath = Paths.get(new URI(classesPath)).getParent();
+			// 2. devtools 配置,因为有 @Configuration 注解的需要 devtools
+			String projectName = projectPath.getFileName().toString();
+			FileObject devToolsFile = filer.createResource(StandardLocation.CLASS_OUTPUT, "", DEVTOOLS_RESOURCE_LOCATION);
+			FactoriesFiles.writeDevToolsFile(projectName, devToolsFile.openOutputStream());
+		} catch (IOException | URISyntaxException e) {
+			fatalError(e);
+		}
+	}
+
+	private boolean isClassOrInterface(Element e) {
+		ElementKind kind = e.getKind();
+		return kind == ElementKind.CLASS || kind == ElementKind.INTERFACE;
+	}
+
+	private boolean isAnnotation(Elements elementUtils, Element e, String annotationFullName) {
+		List<? extends AnnotationMirror> annotationList = elementUtils.getAllAnnotationMirrors(e);
+		for (AnnotationMirror annotation : annotationList) {
+			// 如果是对于的注解
+			if (isAnnotation(annotationFullName, annotation)) {
+				return true;
+			}
+			// 处理组合注解
+			Element element = annotation.getAnnotationType().asElement();
+			// 如果是 java 元注解,继续循环
+			if (element.toString().startsWith("java.lang")) {
+				continue;
+			}
+			// 递归处理 组合注解
+			if (isAnnotation(elementUtils, element, annotationFullName)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	private boolean isAnnotation(String annotationFullName, AnnotationMirror annotation) {
+		return annotationFullName.equals(annotation.getAnnotationType().toString());
+	}
+}

+ 80 - 0
bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/factories/FactoriesFiles.java

@@ -0,0 +1,80 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.auto.factories;
+
+import org.springblade.core.auto.common.MultiSetMap;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Set;
+import java.util.StringJoiner;
+
+/**
+ * spring boot 自动化配置工具类
+ *
+ * @author L.cm
+ */
+class FactoriesFiles {
+	private static final Charset UTF_8 = StandardCharsets.UTF_8;
+
+	/**
+	 * 写出 spring.factories 文件
+	 * @param factories factories 信息
+	 * @param output 输出流
+	 * @throws IOException 异常信息
+	 */
+	static void writeFactoriesFile(MultiSetMap<String, String> factories,
+								   OutputStream output) throws IOException {
+		BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output, UTF_8));
+		Set<String> keySet = factories.keySet();
+		for (String key : keySet) {
+			Set<String> values = factories.get(key);
+			if (values == null || values.isEmpty()) {
+				continue;
+			}
+			writer.write(key);
+			writer.write("=\\\n  ");
+			StringJoiner joiner = new StringJoiner(",\\\n  ");
+			for (String value : values) {
+				joiner.add(value);
+			}
+			writer.write(joiner.toString());
+			writer.newLine();
+		}
+		writer.flush();
+		output.close();
+	}
+
+	/**
+	 * 写出 spring-devtools.properties
+	 * @param projectName 项目名
+	 * @param output 输出流
+	 * @throws IOException 异常信息
+	 */
+	static void writeDevToolsFile(String projectName,
+								  OutputStream output) throws IOException {
+		BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output, UTF_8));
+		String format = "restart.include.%s=/%s[\\\\w-]+\\.jar";
+		writer.write(String.format(format, projectName, projectName));
+		writer.flush();
+		output.close();
+	}
+}

+ 47 - 0
bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/service/AutoService.java

@@ -0,0 +1,47 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.auto.service;
+
+import java.lang.annotation.*;
+
+/**
+ * An annotation for service providers as described in {@link java.util.ServiceLoader}. The {@link
+ * AutoServiceProcessor} generates the configuration files which
+ * allows service providers to be loaded with {@link java.util.ServiceLoader#load(Class)}.
+ *
+ * <p>Service providers assert that they conform to the service provider specification.
+ * Specifically, they must:
+ *
+ * <ul>
+ * <li>be a non-inner, non-anonymous, concrete class
+ * <li>have a publicly accessible no-arg constructor
+ * <li>implement the interface type returned by {@code value()}
+ * </ul>
+ *
+ * @author google
+ */
+@Documented
+@Retention(RetentionPolicy.SOURCE)
+@Target(ElementType.TYPE)
+public @interface AutoService {
+	/**
+	 * Returns the interfaces implemented by this service provider.
+	 *
+	 * @return interface array
+	 */
+	Class<?>[] value();
+}

+ 254 - 0
bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/service/AutoServiceProcessor.java

@@ -0,0 +1,254 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.auto.service;
+
+import org.springblade.core.auto.common.AbstractBladeProcessor;
+import org.springblade.core.auto.common.MultiSetMap;
+import org.springblade.core.auto.common.Sets;
+import org.springblade.core.auto.common.TypeHelper;
+
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedOptions;
+import javax.lang.model.element.*;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.ElementFilter;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.SimpleAnnotationValueVisitor8;
+import javax.lang.model.util.Types;
+import javax.tools.FileObject;
+import javax.tools.StandardLocation;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.annotation.Annotation;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * java spi 服务自动处理器 参考:google auto
+ *
+ * @author L.cm
+ */
+@SupportedOptions("debug")
+public class AutoServiceProcessor extends AbstractBladeProcessor {
+	/**
+	 * spi 服务集合,key 接口 -> value 实现列表
+	 */
+	private final MultiSetMap<String, String> providers = new MultiSetMap<>();
+	private TypeHelper typeHelper;
+
+	@Override
+	public synchronized void init(ProcessingEnvironment env) {
+		super.init(env);
+		this.typeHelper = new TypeHelper(env);
+	}
+
+	@Override
+	public Set<String> getSupportedAnnotationTypes() {
+		return Sets.ofImmutableSet(AutoService.class.getName());
+	}
+
+	@Override
+	protected boolean processImpl(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+		if (roundEnv.processingOver()) {
+			generateConfigFiles();
+		} else {
+			processAnnotations(annotations, roundEnv);
+		}
+		return true;
+	}
+
+	private void processAnnotations(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+		Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(AutoService.class);
+
+		log(annotations.toString());
+		log(elements.toString());
+
+		for (Element e : elements) {
+			TypeElement providerImplementer = (TypeElement) e;
+			AnnotationMirror annotationMirror = getAnnotationMirror(e, AutoService.class);
+			if (annotationMirror == null) {
+				continue;
+			}
+			Set<TypeMirror> typeMirrors = getValueFieldOfClasses(annotationMirror);
+			if (typeMirrors.isEmpty()) {
+				error("No service interfaces provided for element!", e, annotationMirror);
+				continue;
+			}
+			for (TypeMirror typeMirror : typeMirrors) {
+				String providerInterfaceName = typeHelper.getType(typeMirror);
+				Name providerImplementerName = providerImplementer.getQualifiedName();
+
+				log("provider interface: " + providerInterfaceName);
+				log("provider implementer: " + providerImplementerName);
+
+				if (checkImplementer(providerImplementer, typeMirror)) {
+					providers.put(providerInterfaceName, typeHelper.getType(providerImplementer));
+				} else {
+					String message = "ServiceProviders must implement their service provider interface. "
+						+ providerImplementerName + " does not implement " + providerInterfaceName;
+					error(message, e, annotationMirror);
+				}
+			}
+		}
+	}
+
+	private void generateConfigFiles() {
+		Filer filer = processingEnv.getFiler();
+
+		for (String providerInterface : providers.keySet()) {
+			String resourceFile = "META-INF/services/" + providerInterface;
+			log("Working on resource file: " + resourceFile);
+			try {
+				SortedSet<String> allServices = new TreeSet<>();
+				try {
+					FileObject existingFile = filer.getResource(StandardLocation.CLASS_OUTPUT, "", resourceFile);
+					log("Looking for existing resource file at " + existingFile.toUri());
+					Set<String> oldServices = ServicesFiles.readServiceFile(existingFile.openInputStream());
+					log("Existing service entries: " + oldServices);
+					allServices.addAll(oldServices);
+				} catch (IOException e) {
+					log("Resource file did not already exist.");
+				}
+
+				Set<String> newServices = new HashSet<>(providers.get(providerInterface));
+				if (allServices.containsAll(newServices)) {
+					log("No new service entries being added.");
+					return;
+				}
+
+				allServices.addAll(newServices);
+				log("New service file contents: " + allServices);
+				FileObject fileObject = filer.createResource(StandardLocation.CLASS_OUTPUT, "", resourceFile);
+				OutputStream out = fileObject.openOutputStream();
+				ServicesFiles.writeServiceFile(allServices, out);
+				out.close();
+				log("Wrote to: " + fileObject.toUri());
+			} catch (IOException e) {
+				fatalError("Unable to create " + resourceFile + ", " + e);
+				return;
+			}
+		}
+	}
+
+	/**
+	 * Verifies {@link java.util.spi.LocaleServiceProvider} constraints on the concrete provider class.
+	 * Note that these constraints are enforced at runtime via the ServiceLoader,
+	 * we're just checking them at compile time to be extra nice to our users.
+	 */
+	private boolean checkImplementer(TypeElement providerImplementer, TypeMirror providerType) {
+		// TODO: We're currently only enforcing the subtype relationship
+		// constraint. It would be nice to enforce them all.
+		Types types = processingEnv.getTypeUtils();
+
+		return types.isSubtype(providerImplementer.asType(), providerType);
+	}
+
+	/**
+	 * 读取 AutoService 上的 value 值
+	 *
+	 * @param annotationMirror AnnotationMirror
+	 * @return value 集合
+	 */
+	private Set<TypeMirror> getValueFieldOfClasses(AnnotationMirror annotationMirror) {
+		return getAnnotationValue(annotationMirror, "value")
+			.accept(new SimpleAnnotationValueVisitor8<Set<TypeMirror>, Void>() {
+				@Override
+				public Set<TypeMirror> visitType(TypeMirror typeMirror, Void v) {
+					Set<TypeMirror> declaredTypeSet = new HashSet<>(1);
+					declaredTypeSet.add(typeMirror);
+					return Collections.unmodifiableSet(declaredTypeSet);
+				}
+
+				@Override
+				public Set<TypeMirror> visitArray(
+					List<? extends AnnotationValue> values, Void v) {
+					return values
+						.stream()
+						.flatMap(value -> value.accept(this, null).stream())
+						.collect(Collectors.toSet());
+				}
+			}, null);
+	}
+
+	/**
+	 * Returns a {@link ExecutableElement} and its associated {@link AnnotationValue} if such
+	 * an element was either declared in the usage represented by the provided
+	 * {@link AnnotationMirror}, or if such an element was defined with a default.
+	 *
+	 * @param annotationMirror AnnotationMirror
+	 * @param elementName      elementName
+	 * @return AnnotationValue map
+	 * @throws IllegalArgumentException if no element is defined with the given elementName.
+	 */
+	public AnnotationValue getAnnotationValue(AnnotationMirror annotationMirror, String elementName) {
+		Objects.requireNonNull(annotationMirror);
+		Objects.requireNonNull(elementName);
+		for (Map.Entry<ExecutableElement, AnnotationValue> entry : getAnnotationValuesWithDefaults(annotationMirror).entrySet()) {
+			if (entry.getKey().getSimpleName().contentEquals(elementName)) {
+				return entry.getValue();
+			}
+		}
+		String name = typeHelper.getType(annotationMirror);
+		throw new IllegalArgumentException(String.format("@%s does not define an element %s()", name, elementName));
+	}
+
+	/**
+	 * Returns the {@link AnnotationMirror}'s map of {@link AnnotationValue} indexed by {@link
+	 * ExecutableElement}, supplying default values from the annotation if the annotation property has
+	 * not been set. This is equivalent to {@link
+	 * Elements#getElementValuesWithDefaults(AnnotationMirror)} but can be called statically without
+	 * an {@link Elements} instance.
+	 *
+	 * <p>The iteration order of elements of the returned map will be the order in which the {@link
+	 * ExecutableElement}s are defined in {@code annotation}'s {@linkplain
+	 * AnnotationMirror#getAnnotationType() type}.
+	 *
+	 * @param annotation AnnotationMirror
+	 * @return AnnotationValue Map
+	 */
+	public Map<ExecutableElement, AnnotationValue> getAnnotationValuesWithDefaults(AnnotationMirror annotation) {
+		Map<ExecutableElement, AnnotationValue> values = new HashMap<>(32);
+		Map<? extends ExecutableElement, ? extends AnnotationValue> declaredValues = annotation.getElementValues();
+		for (ExecutableElement method : ElementFilter.methodsIn(annotation.getAnnotationType().asElement().getEnclosedElements())) {
+			// Must iterate and put in this order, to ensure consistency in generated code.
+			if (declaredValues.containsKey(method)) {
+				values.put(method, declaredValues.get(method));
+			} else if (method.getDefaultValue() != null) {
+				values.put(method, method.getDefaultValue());
+			} else {
+				String name = typeHelper.getType(method);
+				throw new IllegalStateException(
+					"Unset annotation value without default should never happen: " + name + '.' + method.getSimpleName() + "()");
+			}
+		}
+		return Collections.unmodifiableMap(values);
+	}
+
+	public AnnotationMirror getAnnotationMirror(Element element, Class<? extends Annotation> annotationClass) {
+		String annotationClassName = annotationClass.getCanonicalName();
+		for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) {
+			String name = typeHelper.getType(annotationMirror);
+			if (name.contentEquals(annotationClassName)) {
+				return annotationMirror;
+			}
+		}
+		return null;
+	}
+
+}

+ 77 - 0
bladex-tool/blade-core-auto/src/main/java/org/springblade/core/auto/service/ServicesFiles.java

@@ -0,0 +1,77 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.auto.service;
+
+import java.io.*;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A helper class for reading and writing Services files.
+ *
+ * @author L.cm
+ */
+class ServicesFiles {
+	private static final Charset UTF_8 = StandardCharsets.UTF_8;
+
+	/**
+	 * Reads the set of service classes from a service file.
+	 *
+	 * @param input not {@code null}. Closed after use.
+	 * @return a not {@code null Set} of service class names.
+	 * @throws IOException
+	 */
+	static Set<String> readServiceFile(InputStream input) throws IOException {
+		HashSet<String> serviceClasses = new HashSet<>();
+		try (
+			InputStreamReader isr = new InputStreamReader(input, UTF_8);
+			BufferedReader r = new BufferedReader(isr)
+		) {
+			String line;
+			while ((line = r.readLine()) != null) {
+				int commentStart = line.indexOf('#');
+				if (commentStart >= 0) {
+					line = line.substring(0, commentStart);
+				}
+				line = line.trim();
+				if (!line.isEmpty()) {
+					serviceClasses.add(line);
+				}
+			}
+			return serviceClasses;
+		}
+	}
+
+	/**
+	 * Writes the set of service class names to a service file.
+	 *
+	 * @param output   not {@code null}. Not closed after use.
+	 * @param services a not {@code null Collection} of service class names.
+	 * @throws IOException
+	 */
+	static void writeServiceFile(Collection<String> services, OutputStream output) throws IOException {
+		BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output, UTF_8));
+		for (String service : services) {
+			writer.write(service);
+			writer.newLine();
+		}
+		writer.flush();
+	}
+}

+ 2 - 0
bladex-tool/blade-core-auto/src/main/resources/META-INF/services/javax.annotation.processing.Processor

@@ -0,0 +1,2 @@
+org.springblade.core.auto.service.AutoServiceProcessor
+org.springblade.core.auto.factories.AutoFactoriesProcessor

+ 60 - 0
bladex-tool/blade-core-boot/pom.xml

@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<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>
+        <groupId>org.springblade</groupId>
+        <artifactId>BladeX-Tool</artifactId>
+        <version>2.8.2.RELEASE</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-core-boot</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${project.parent.version}</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <!-- Blade -->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-context</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-db</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-secure</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-cloud</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-cache</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-redis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-log</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-mybatis</artifactId>
+        </dependency>
+        <!-- Auto -->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-auto</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>

+ 37 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/config/BladeBootAutoConfiguration.java

@@ -0,0 +1,37 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.boot.config;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.core.launch.props.BladePropertySource;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+
+/**
+ * blade自动配置类
+ *
+ * @author Chill
+ */
+@Slf4j
+@Configuration(proxyBeanMethods = false)
+@AllArgsConstructor
+@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
+@BladePropertySource(value = "classpath:/blade-boot.yml")
+public class BladeBootAutoConfiguration {
+
+}

+ 132 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/config/BladeExecutorConfiguration.java

@@ -0,0 +1,132 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.boot.config;
+
+import lombok.AllArgsConstructor;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.core.boot.error.ErrorType;
+import org.springblade.core.boot.error.ErrorUtil;
+import org.springblade.core.context.BladeContext;
+import org.springblade.core.context.BladeRunnableWrapper;
+import org.springblade.core.launch.props.BladeProperties;
+import org.springblade.core.log.constant.EventConstant;
+import org.springblade.core.log.event.ErrorLogEvent;
+import org.springblade.core.log.model.LogError;
+import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
+import org.springframework.boot.task.TaskExecutorCustomizer;
+import org.springframework.boot.task.TaskSchedulerCustomizer;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.lang.NonNull;
+import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.util.ErrorHandler;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ThreadPoolExecutor;
+
+/**
+ * 异步处理
+ *
+ * @author Chill
+ */
+@Slf4j
+@Configuration
+@EnableAsync
+@EnableScheduling
+@AllArgsConstructor
+public class BladeExecutorConfiguration extends AsyncConfigurerSupport {
+
+	private final BladeContext bladeContext;
+	private final BladeProperties bladeProperties;
+	private final ApplicationEventPublisher publisher;
+
+	@Bean
+	public TaskExecutorCustomizer taskExecutorCustomizer() {
+		return taskExecutor -> {
+			taskExecutor.setThreadNamePrefix("async-task-");
+			taskExecutor.setTaskDecorator(BladeRunnableWrapper::new);
+			taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+		};
+	}
+
+	@Bean
+	public TaskSchedulerCustomizer taskSchedulerCustomizer() {
+		return taskExecutor -> {
+			taskExecutor.setThreadNamePrefix("async-scheduler");
+			taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
+			taskExecutor.setErrorHandler(new BladeErrorHandler(bladeContext, bladeProperties, publisher));
+		};
+	}
+
+	@Override
+	public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
+		return new BladeAsyncUncaughtExceptionHandler(bladeContext, bladeProperties, publisher);
+	}
+
+	@RequiredArgsConstructor
+	private static class BladeAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
+		private final BladeContext bladeContext;
+		private final BladeProperties bladeProperties;
+		private final ApplicationEventPublisher eventPublisher;
+
+		@Override
+		public void handleUncaughtException(@NonNull Throwable error, @NonNull Method method, @NonNull Object... params) {
+			log.error("Unexpected exception occurred invoking async method: {}", method, error);
+			LogError logError = new LogError();
+			// 服务信息、环境、异常类型
+			logError.setParams(ErrorType.ASYNC.getType());
+			logError.setEnv(bladeProperties.getEnv());
+			logError.setServiceId(bladeProperties.getName());
+			logError.setRequestUri(bladeContext.getRequestId());
+			// 堆栈信息
+			ErrorUtil.initErrorInfo(error, logError);
+			Map<String, Object> event = new HashMap<>(16);
+			event.put(EventConstant.EVENT_LOG, logError);
+			eventPublisher.publishEvent(new ErrorLogEvent(event));
+		}
+	}
+
+	@RequiredArgsConstructor
+	private static class BladeErrorHandler implements ErrorHandler {
+		private final BladeContext bladeContext;
+		private final BladeProperties bladeProperties;
+		private final ApplicationEventPublisher eventPublisher;
+
+		@Override
+		public void handleError(@NonNull Throwable error) {
+			log.error("Unexpected scheduler exception", error);
+			LogError logError = new LogError();
+			// 服务信息、环境、异常类型
+			logError.setParams(ErrorType.SCHEDULER.getType());
+			logError.setServiceId(bladeProperties.getName());
+			logError.setEnv(bladeProperties.getEnv());
+			logError.setRequestUri(bladeContext.getRequestId());
+			// 堆栈信息
+			ErrorUtil.initErrorInfo(error, logError);
+			Map<String, Object> event = new HashMap<>(16);
+			event.put(EventConstant.EVENT_LOG, logError);
+			eventPublisher.publishEvent(new ErrorLogEvent(event));
+		}
+	}
+
+}

+ 49 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/config/BladeRetryConfiguration.java

@@ -0,0 +1,49 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.boot.config;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.retry.interceptor.RetryInterceptorBuilder;
+import org.springframework.retry.interceptor.RetryOperationsInterceptor;
+
+/**
+ * 重试机制
+ *
+ * @author Chill
+ */
+@Slf4j
+@Configuration(proxyBeanMethods = false)
+public class BladeRetryConfiguration {
+
+	@Bean
+	@ConditionalOnMissingBean(name = "configServerRetryInterceptor")
+	public RetryOperationsInterceptor configServerRetryInterceptor() {
+		log.info(String.format(
+			"configServerRetryInterceptor: Changing backOffOptions " +
+				"to initial: %s, multiplier: %s, maxInterval: %s",
+			1000, 1.2, 5000));
+		return RetryInterceptorBuilder
+			.stateless()
+			.backOffOptions(1000, 1.2, 5000)
+			.maxAttempts(10)
+			.build();
+	}
+
+}

+ 62 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/config/BladeWebMvcConfiguration.java

@@ -0,0 +1,62 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.boot.config;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.core.boot.props.BladeFileProperties;
+import org.springblade.core.boot.props.BladeUploadProperties;
+import org.springblade.core.boot.resolver.TokenArgumentResolver;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.util.List;
+
+/**
+ * WEB配置
+ *
+ * @author Chill
+ */
+@Slf4j
+@Configuration(proxyBeanMethods = false)
+@Order(Ordered.HIGHEST_PRECEDENCE)
+@AllArgsConstructor
+@EnableConfigurationProperties({
+	BladeUploadProperties.class, BladeFileProperties.class
+})
+public class BladeWebMvcConfiguration implements WebMvcConfigurer {
+
+	private final BladeUploadProperties bladeUploadProperties;
+
+	@Override
+	public void addResourceHandlers(ResourceHandlerRegistry registry) {
+		String path = bladeUploadProperties.getSavePath();
+		registry.addResourceHandler("/upload/**")
+			.addResourceLocations("file:" + path + "/upload/");
+	}
+
+	@Override
+	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
+		argumentResolvers.add(new TokenArgumentResolver());
+	}
+
+}

+ 57 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/config/RequestConfiguration.java

@@ -0,0 +1,57 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.boot.config;
+
+import lombok.AllArgsConstructor;
+import org.springblade.core.boot.request.BladeRequestFilter;
+import org.springblade.core.boot.request.RequestProperties;
+import org.springblade.core.boot.request.XssProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+
+import javax.servlet.DispatcherType;
+
+/**
+ * 过滤器配置类
+ *
+ * @author Chill
+ */
+@Configuration(proxyBeanMethods = false)
+@AllArgsConstructor
+@EnableConfigurationProperties({RequestProperties.class, XssProperties.class})
+public class RequestConfiguration {
+
+	private final RequestProperties requestProperties;
+	private final XssProperties xssProperties;
+
+	/**
+	 * 全局过滤器
+	 */
+	@Bean
+	public FilterRegistrationBean<BladeRequestFilter> bladeFilterRegistration() {
+		FilterRegistrationBean<BladeRequestFilter> registration = new FilterRegistrationBean<>();
+		registration.setDispatcherTypes(DispatcherType.REQUEST);
+		registration.setFilter(new BladeRequestFilter(requestProperties, xssProperties));
+		registration.addUrlPatterns("/*");
+		registration.setName("bladeRequestFilter");
+		registration.setOrder(Ordered.LOWEST_PRECEDENCE);
+		return registration;
+	}
+}

+ 279 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/ctrl/BladeController.java

@@ -0,0 +1,279 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.boot.ctrl;
+
+import org.springblade.core.boot.file.LocalFile;
+import org.springblade.core.boot.file.BladeFileUtil;
+import org.springblade.core.secure.BladeUser;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.utils.Charsets;
+import org.springblade.core.tool.utils.StringPool;
+import org.springblade.core.tool.utils.WebUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.ResourceRegion;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.util.UriUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Blade控制器封装类
+ *
+ * @author Chill
+ */
+public class BladeController {
+
+	/**
+	 * ============================     REQUEST    =================================================
+	 */
+
+	@Autowired
+	private HttpServletRequest request;
+
+	/**
+	 * 获取request
+	 */
+	public HttpServletRequest getRequest() {
+		return this.request;
+	}
+
+	/**
+	 * 获取当前用户
+	 *
+	 * @return
+	 */
+	public BladeUser getUser() {
+		return AuthUtil.getUser();
+	}
+
+	/** ============================     API_RESULT    =================================================  */
+
+	/**
+	 * 返回ApiResult
+	 *
+	 * @param data
+	 * @return R
+	 */
+	public <T> R<T> data(T data) {
+		return R.data(data);
+	}
+
+	/**
+	 * 返回ApiResult
+	 *
+	 * @param data
+	 * @param message
+	 * @return R
+	 */
+	public <T> R<T> data(T data, String message) {
+		return R.data(data, message);
+	}
+
+	/**
+	 * 返回ApiResult
+	 *
+	 * @param data
+	 * @param message
+	 * @param code
+	 * @return R
+	 */
+	public <T> R<T> data(T data, String message, int code) {
+		return R.data(code, data, message);
+	}
+
+	/**
+	 * 返回ApiResult
+	 *
+	 * @param message
+	 * @return R
+	 */
+	public R success(String message) {
+		return R.success(message);
+	}
+
+	/**
+	 * 返回ApiResult
+	 *
+	 * @param message
+	 * @return R
+	 */
+	public R fail(String message) {
+		return R.fail(message);
+	}
+
+	/**
+	 * 返回ApiResult
+	 *
+	 * @param flag
+	 * @return R
+	 */
+	public R status(boolean flag) {
+		return R.status(flag);
+	}
+
+
+	/**============================     FILE    =================================================  */
+
+	/**
+	 * 获取BladeFile封装类
+	 *
+	 * @param file
+	 * @return
+	 */
+	public LocalFile getFile(MultipartFile file) {
+		return BladeFileUtil.getFile(file);
+	}
+
+	/**
+	 * 获取BladeFile封装类
+	 *
+	 * @param file
+	 * @param dir
+	 * @return
+	 */
+	public LocalFile getFile(MultipartFile file, String dir) {
+		return BladeFileUtil.getFile(file, dir);
+	}
+
+	/**
+	 * 获取BladeFile封装类
+	 *
+	 * @param file
+	 * @param dir
+	 * @param path
+	 * @param virtualPath
+	 * @return
+	 */
+	public LocalFile getFile(MultipartFile file, String dir, String path, String virtualPath) {
+		return BladeFileUtil.getFile(file, dir, path, virtualPath);
+	}
+
+	/**
+	 * 获取BladeFile封装类
+	 *
+	 * @param files
+	 * @return
+	 */
+	public List<LocalFile> getFiles(List<MultipartFile> files) {
+		return BladeFileUtil.getFiles(files);
+	}
+
+	/**
+	 * 获取BladeFile封装类
+	 *
+	 * @param files
+	 * @param dir
+	 * @return
+	 */
+	public List<LocalFile> getFiles(List<MultipartFile> files, String dir) {
+		return BladeFileUtil.getFiles(files, dir);
+	}
+
+	/**
+	 * 获取BladeFile封装类
+	 *
+	 * @param files
+	 * @param path
+	 * @param virtualPath
+	 * @return
+	 */
+	public List<LocalFile> getFiles(List<MultipartFile> files, String dir, String path, String virtualPath) {
+		return BladeFileUtil.getFiles(files, dir, path, virtualPath);
+	}
+	/**
+	 * 下载文件
+	 *
+	 * @param file 文件
+	 * @return {ResponseEntity}
+	 * @throws IOException io异常
+	 */
+	protected ResponseEntity<ResourceRegion> download(File file) throws IOException {
+		String fileName = file.getName();
+		return download(file, fileName);
+	}
+
+	/**
+	 * 下载
+	 *
+	 * @param file     文件
+	 * @param fileName 生成的文件名
+	 * @return {ResponseEntity}
+	 * @throws IOException io异常
+	 */
+	protected ResponseEntity<ResourceRegion> download(File file, String fileName) throws IOException {
+		Resource resource = new FileSystemResource(file);
+		return download(resource, fileName);
+	}
+
+	/**
+	 * 下载
+	 *
+	 * @param resource 资源
+	 * @param fileName 生成的文件名
+	 * @return {ResponseEntity}
+	 * @throws IOException io异常
+	 */
+	protected ResponseEntity<ResourceRegion> download(Resource resource, String fileName) throws IOException {
+		HttpServletRequest request = WebUtil.getRequest();
+		String header = request.getHeader(HttpHeaders.USER_AGENT);
+		// 避免空指针
+		header = header == null ? StringPool.EMPTY : header.toUpperCase();
+		HttpStatus status;
+		String msie= "MSIE";
+		String trident= "TRIDENT";
+		String edge= "EDGE";
+		if (header.contains(msie) || header.contains(trident) || header.contains(edge)) {
+			status = HttpStatus.OK;
+		} else {
+			status = HttpStatus.CREATED;
+		}
+		// 断点续传
+		long position = 0;
+		long count = resource.contentLength();
+		String range = request.getHeader(HttpHeaders.RANGE);
+		if (null != range) {
+			status = HttpStatus.PARTIAL_CONTENT;
+			String[] rangeRange = range.replace("bytes=", StringPool.EMPTY).split(StringPool.DASH);
+			position = Long.parseLong(rangeRange[0]);
+			if (rangeRange.length > 1) {
+				long end = Long.parseLong(rangeRange[1]);
+				count = end - position + 1;
+			}
+		}
+		HttpHeaders headers = new HttpHeaders();
+		headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
+		String encodeFileName = UriUtils.encode(fileName, Charsets.UTF_8);
+		// 兼容各种浏览器下载:
+		// https://blog.robotshell.org/2012/deal-with-http-header-encoding-for-file-download/
+		String disposition = "attachment;" +
+			"filename=\"" + encodeFileName + "\";" +
+			"filename*=utf-8''" + encodeFileName;
+		headers.set(HttpHeaders.CONTENT_DISPOSITION, disposition);
+		return new ResponseEntity<>(new ResourceRegion(resource, position, count), headers, status);
+	}
+
+}

+ 57 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/error/ErrorType.java

@@ -0,0 +1,57 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.boot.error;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.springframework.lang.Nullable;
+
+/**
+ * 异常类型
+ *
+ * @author L.cm
+ */
+@Getter
+@RequiredArgsConstructor
+public enum ErrorType {
+	/**
+	 * 异常类型
+	 */
+	REQUEST("request"),
+	ASYNC("async"),
+	SCHEDULER("scheduler"),
+	WEB_SOCKET("websocket"),
+	OTHER("other");
+
+	@JsonValue
+	private final String type;
+
+	@Nullable
+	@JsonCreator
+	public static ErrorType of(String type) {
+		ErrorType[] values = ErrorType.values();
+		for (ErrorType errorType : values) {
+			if (errorType.type.equals(type)) {
+				return errorType;
+			}
+		}
+		return null;
+	}
+
+}

+ 53 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/error/ErrorUtil.java

@@ -0,0 +1,53 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.boot.error;
+
+import org.springblade.core.log.model.LogError;
+import org.springblade.core.tool.utils.DateUtil;
+import org.springblade.core.tool.utils.Exceptions;
+import org.springblade.core.tool.utils.ObjectUtil;
+
+/**
+ * 异常工具类
+ *
+ * @author L.cm
+ */
+public class ErrorUtil {
+
+	/**
+	 * 初始化异常信息
+	 *
+	 * @param error 异常
+	 * @param event 异常事件封装
+	 */
+	public static void initErrorInfo(Throwable error, LogError event) {
+		// 堆栈信息
+		event.setStackTrace(Exceptions.getStackTraceAsString(error));
+		event.setExceptionName(error.getClass().getName());
+		event.setMessage(error.getMessage());
+		event.setCreateTime(DateUtil.now());
+		StackTraceElement[] elements = error.getStackTrace();
+		if (ObjectUtil.isNotEmpty(elements)) {
+			// 报错的类信息
+			StackTraceElement element = elements[0];
+			event.setMethodClass(element.getClassName());
+			event.setFileName(element.getFileName());
+			event.setMethodName(element.getMethodName());
+			event.setLineNumber(element.getLineNumber());
+		}
+	}
+}

+ 240 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/file/BladeFileUtil.java

@@ -0,0 +1,240 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.boot.file;
+
+import org.springblade.core.tool.utils.StringPool;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.*;
+
+/**
+ * 文件工具类
+ *
+ * @author Chill
+ */
+public class BladeFileUtil {
+
+	/**
+	 * 定义允许上传的文件扩展名
+	 */
+	private static final HashMap<String, String> EXT_MAP = new HashMap<>();
+	private static final String IS_DIR = "is_dir";
+	private static final String FILE_NAME = "filename";
+	private static final String FILE_SIZE = "filesize";
+
+	/**
+	 * 图片扩展名
+	 */
+	private static final String[] FILE_TYPES = new String[]{"gif", "jpg", "jpeg", "png", "bmp"};
+
+	static {
+		EXT_MAP.put("image", ".gif,.jpg,.jpeg,.png,.bmp,.JPG,.JPEG,.PNG");
+		EXT_MAP.put("flash", ".swf,.flv");
+		EXT_MAP.put("media", ".swf,.flv,.mp3,.mp4,.wav,.wma,.wmv,.mid,.avi,.mpg,.asf,.rm,.rmvb");
+		EXT_MAP.put("file", ".doc,.docx,.xls,.xlsx,.ppt,.htm,.html,.txt,.zip,.rar,.gz,.bz2");
+		EXT_MAP.put("allfile", ".gif,.jpg,.jpeg,.png,.bmp,.swf,.flv,.mp3,.mp4,.wav,.wma,.wmv,.mid,.avi,.mpg,.asf,.rm,.rmvb,.doc,.docx,.xls,.xlsx,.ppt,.htm,.html,.txt,.zip,.rar,.gz,.bz2");
+	}
+
+	/**
+	 * 获取文件后缀
+	 *
+	 * @param fileName 文件名
+	 * @return String 返回后缀
+	 */
+	public static String getFileExt(String fileName) {
+		return fileName.substring(fileName.lastIndexOf(StringPool.DOT));
+	}
+
+	/**
+	 * 测试文件后缀 只让指定后缀的文件上传,像jsp,war,sh等危险的后缀禁止
+	 *
+	 * @param dir      目录
+	 * @param fileName 文件名
+	 * @return 返回成功与否
+	 */
+	public static boolean testExt(String dir, String fileName) {
+		String fileExt = getFileExt(fileName);
+		String ext = EXT_MAP.get(dir);
+		return StringUtil.isNotBlank(ext) && (ext.contains(fileExt.toLowerCase()) || ext.contains(fileExt.toUpperCase()));
+	}
+
+	/**
+	 * 文件管理排序
+	 */
+	public enum FileSort {
+
+		/**
+		 * 大小
+		 */
+		size,
+
+		/**
+		 * 类型
+		 */
+		type,
+
+		/**
+		 * 名称
+		 */
+		name;
+
+		/**
+		 * 文本排序转换成枚举
+		 *
+		 * @param sort
+		 * @return
+		 */
+		public static FileSort of(String sort) {
+			try {
+				return FileSort.valueOf(sort);
+			} catch (Exception e) {
+				return FileSort.name;
+			}
+		}
+	}
+
+	public static class NameComparator implements Comparator {
+		@Override
+		public int compare(Object a, Object b) {
+			Hashtable hashA = (Hashtable) a;
+			Hashtable hashB = (Hashtable) b;
+			if (((Boolean) hashA.get(IS_DIR)) && !((Boolean) hashB.get(IS_DIR))) {
+				return -1;
+			} else if (!((Boolean) hashA.get(IS_DIR)) && ((Boolean) hashB.get(IS_DIR))) {
+				return 1;
+			} else {
+				return ((String) hashA.get(FILE_NAME)).compareTo((String) hashB.get(FILE_NAME));
+			}
+		}
+	}
+
+	public static class SizeComparator implements Comparator {
+		@Override
+		public int compare(Object a, Object b) {
+			Hashtable hashA = (Hashtable) a;
+			Hashtable hashB = (Hashtable) b;
+			if (((Boolean) hashA.get(IS_DIR)) && !((Boolean) hashB.get(IS_DIR))) {
+				return -1;
+			} else if (!((Boolean) hashA.get(IS_DIR)) && ((Boolean) hashB.get(IS_DIR))) {
+				return 1;
+			} else {
+				if (((Long) hashA.get(FILE_SIZE)) > ((Long) hashB.get(FILE_SIZE))) {
+					return 1;
+				} else if (((Long) hashA.get(FILE_SIZE)) < ((Long) hashB.get(FILE_SIZE))) {
+					return -1;
+				} else {
+					return 0;
+				}
+			}
+		}
+	}
+
+	public static class TypeComparator implements Comparator {
+		@Override
+		public int compare(Object a, Object b) {
+			Hashtable hashA = (Hashtable) a;
+			Hashtable hashB = (Hashtable) b;
+			if (((Boolean) hashA.get(IS_DIR)) && !((Boolean) hashB.get(IS_DIR))) {
+				return -1;
+			} else if (!((Boolean) hashA.get(IS_DIR)) && ((Boolean) hashB.get(IS_DIR))) {
+				return 1;
+			} else {
+				return ((String) hashA.get("filetype")).compareTo((String) hashB.get("filetype"));
+			}
+		}
+	}
+
+	public static String formatUrl(String url) {
+		return url.replaceAll("\\\\", "/");
+	}
+
+
+	/********************************BladeFile封装********************************************************/
+
+	/**
+	 * 获取BladeFile封装类
+	 *
+	 * @param file 文件
+	 * @return BladeFile
+	 */
+	public static LocalFile getFile(MultipartFile file) {
+		return getFile(file, "image", null, null);
+	}
+
+	/**
+	 * 获取BladeFile封装类
+	 *
+	 * @param file 文件
+	 * @param dir  目录
+	 * @return BladeFile
+	 */
+	public static LocalFile getFile(MultipartFile file, String dir) {
+		return getFile(file, dir, null, null);
+	}
+
+	/**
+	 * 获取BladeFile封装类
+	 *
+	 * @param file        文件
+	 * @param dir         目录
+	 * @param path        路径
+	 * @param virtualPath 虚拟路径
+	 * @return BladeFile
+	 */
+	public static LocalFile getFile(MultipartFile file, String dir, String path, String virtualPath) {
+		return new LocalFile(file, dir, path, virtualPath);
+	}
+
+	/**
+	 * 获取BladeFile封装类
+	 *
+	 * @param files 文件集合
+	 * @return BladeFile
+	 */
+	public static List<LocalFile> getFiles(List<MultipartFile> files) {
+		return getFiles(files, "image", null, null);
+	}
+
+	/**
+	 * 获取BladeFile封装类
+	 *
+	 * @param files 文件集合
+	 * @param dir   目录
+	 * @return BladeFile
+	 */
+	public static List<LocalFile> getFiles(List<MultipartFile> files, String dir) {
+		return getFiles(files, dir, null, null);
+	}
+
+	/**
+	 * 获取BladeFile封装类
+	 *
+	 * @param files       文件集合
+	 * @param path        路径
+	 * @param virtualPath 虚拟路径
+	 * @return BladeFile
+	 */
+	public static List<LocalFile> getFiles(List<MultipartFile> files, String dir, String path, String virtualPath) {
+		List<LocalFile> list = new ArrayList<>();
+		for (MultipartFile file : files) {
+			list.add(new LocalFile(file, dir, path, virtualPath));
+		}
+		return list;
+	}
+
+}

+ 51 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/file/FileProxyManager.java

@@ -0,0 +1,51 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.boot.file;
+
+import java.io.File;
+
+/**
+ * 文件管理类
+ *
+ * @author Chill
+ */
+public class FileProxyManager {
+	private IFileProxy defaultFileProxyFactory = new LocalFileProxyFactory();
+
+	private static final FileProxyManager ME = new FileProxyManager();
+
+	public static FileProxyManager me() {
+		return ME;
+	}
+
+	public IFileProxy getDefaultFileProxyFactory() {
+		return defaultFileProxyFactory;
+	}
+
+	public void setDefaultFileProxyFactory(IFileProxy defaultFileProxyFactory) {
+		this.defaultFileProxyFactory = defaultFileProxyFactory;
+	}
+
+	public String[] path(File file, String dir) {
+		return defaultFileProxyFactory.path(file, dir);
+	}
+
+	public File rename(File file, String path) {
+		return defaultFileProxyFactory.rename(file, path);
+	}
+
+}

+ 53 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/file/IFileProxy.java

@@ -0,0 +1,53 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.boot.file;
+
+import java.io.File;
+
+/**
+ * 文件代理接口
+ *
+ * @author Chill
+ */
+public interface IFileProxy {
+
+	/**
+	 * 返回路径[物理路径][虚拟路径]
+	 *
+	 * @param file 文件
+	 * @param dir  目录
+	 * @return
+	 */
+	String[] path(File file, String dir);
+
+	/**
+	 * 文件重命名策略
+	 *
+	 * @param file 文件
+	 * @param path 路径
+	 * @return
+	 */
+	File rename(File file, String path);
+
+	/**
+	 * 图片压缩
+	 *
+	 * @param path 路径
+	 */
+	void compress(String path);
+
+}

+ 157 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/file/LocalFile.java

@@ -0,0 +1,157 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.boot.file;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Data;
+import org.springblade.core.boot.props.BladeFileProperties;
+import org.springblade.core.tool.utils.DateUtil;
+import org.springblade.core.tool.utils.SpringUtil;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * 上传文件封装
+ *
+ * @author Chill
+ */
+@Data
+public class LocalFile {
+	/**
+	 * 上传文件在附件表中的id
+	 */
+	private Object fileId;
+
+	/**
+	 * 上传文件
+	 */
+	@JsonIgnore
+	private MultipartFile file;
+
+	/**
+	 * 文件外网地址
+	 */
+	private String domain;
+
+	/**
+	 * 上传分类文件夹
+	 */
+	private String dir;
+
+	/**
+	 * 上传物理路径
+	 */
+	private String uploadPath;
+
+	/**
+	 * 上传虚拟路径
+	 */
+	private String uploadVirtualPath;
+
+	/**
+	 * 文件名
+	 */
+	private String fileName;
+
+	/**
+	 * 真实文件名
+	 */
+	private String originalFileName;
+
+	/**
+	 * 文件配置
+	 */
+	private static BladeFileProperties fileProperties;
+
+	private static BladeFileProperties getBladeFileProperties() {
+		if (fileProperties == null) {
+			fileProperties = SpringUtil.getBean(BladeFileProperties.class);
+		}
+		return fileProperties;
+	}
+
+	public LocalFile(MultipartFile file, String dir) {
+		this.dir = dir;
+		this.file = file;
+		this.fileName = file.getName();
+		this.originalFileName = file.getOriginalFilename();
+		this.domain = getBladeFileProperties().getUploadDomain();
+		this.uploadPath = BladeFileUtil.formatUrl(File.separator + getBladeFileProperties().getUploadRealPath() + File.separator + dir + File.separator + DateUtil.format(DateUtil.now(), "yyyyMMdd") + File.separator + this.originalFileName);
+		this.uploadVirtualPath = BladeFileUtil.formatUrl(getBladeFileProperties().getUploadCtxPath().replace(getBladeFileProperties().getContextPath(), "") + File.separator + dir + File.separator + DateUtil.format(DateUtil.now(), "yyyyMMdd") + File.separator + this.originalFileName);
+	}
+
+	public LocalFile(MultipartFile file, String dir, String uploadPath, String uploadVirtualPath) {
+		this(file, dir);
+		if (null != uploadPath) {
+			this.uploadPath = BladeFileUtil.formatUrl(uploadPath);
+			this.uploadVirtualPath = BladeFileUtil.formatUrl(uploadVirtualPath);
+		}
+	}
+
+	/**
+	 * 图片上传
+	 */
+	public void transfer() {
+		transfer(getBladeFileProperties().getCompress());
+	}
+
+	/**
+	 * 图片上传
+	 *
+	 * @param compress 是否压缩
+	 */
+	public void transfer(boolean compress) {
+		IFileProxy fileFactory = FileProxyManager.me().getDefaultFileProxyFactory();
+		this.transfer(fileFactory, compress);
+	}
+
+	/**
+	 * 图片上传
+	 *
+	 * @param fileFactory 文件上传工厂类
+	 * @param compress    是否压缩
+	 */
+	public void transfer(IFileProxy fileFactory, boolean compress) {
+		try {
+			File file = new File(uploadPath);
+
+			if (null != fileFactory) {
+				String[] path = fileFactory.path(file, dir);
+				this.uploadPath = path[0];
+				this.uploadVirtualPath = path[1];
+				file = fileFactory.rename(file, path[0]);
+			}
+
+			File pfile = file.getParentFile();
+			if (!pfile.exists()) {
+				pfile.mkdirs();
+			}
+
+			this.file.transferTo(file);
+
+			if (compress) {
+				fileFactory.compress(this.uploadPath);
+			}
+
+		} catch (IllegalStateException | IOException e) {
+			e.printStackTrace();
+		}
+	}
+
+}

+ 117 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/file/LocalFileProxyFactory.java

@@ -0,0 +1,117 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.boot.file;
+
+import org.springblade.core.boot.props.BladeFileProperties;
+import org.springblade.core.tool.utils.DateUtil;
+import org.springblade.core.tool.utils.ImageUtil;
+import org.springblade.core.tool.utils.SpringUtil;
+import org.springblade.core.tool.utils.StringPool;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+
+/**
+ * 文件代理类
+ *
+ * @author Chill
+ */
+public class LocalFileProxyFactory implements IFileProxy {
+
+	/**
+	 * 文件配置
+	 */
+	private static BladeFileProperties fileProperties;
+
+	private static BladeFileProperties getBladeFileProperties() {
+		if (fileProperties == null) {
+			fileProperties = SpringUtil.getBean(BladeFileProperties.class);
+		}
+		return fileProperties;
+	}
+
+	@Override
+	public File rename(File f, String path) {
+		File dest = new File(path);
+		f.renameTo(dest);
+		return dest;
+	}
+
+	@Override
+	public String[] path(File f, String dir) {
+		//避免网络延迟导致时间不同步
+		long time = System.nanoTime();
+
+		StringBuilder uploadPath = new StringBuilder()
+			.append(getFileDir(dir, getBladeFileProperties().getUploadRealPath()))
+			.append(time)
+			.append(getFileExt(f.getName()));
+
+		StringBuilder virtualPath = new StringBuilder()
+			.append(getFileDir(dir, getBladeFileProperties().getUploadCtxPath()))
+			.append(time)
+			.append(getFileExt(f.getName()));
+
+		return new String[]{BladeFileUtil.formatUrl(uploadPath.toString()), BladeFileUtil.formatUrl(virtualPath.toString())};
+	}
+
+	/**
+	 * 获取文件后缀
+	 *
+	 * @param fileName 文件名
+	 * @return 文件后缀
+	 */
+	public static String getFileExt(String fileName) {
+		if (!fileName.contains(StringPool.DOT)) {
+			return ".jpg";
+		} else {
+			return fileName.substring(fileName.lastIndexOf(StringPool.DOT));
+		}
+	}
+
+	/**
+	 * 获取文件保存地址
+	 *
+	 * @param dir     目录
+	 * @param saveDir 保存目录
+	 * @return 地址
+	 */
+	public static String getFileDir(String dir, String saveDir) {
+		StringBuilder newFileDir = new StringBuilder();
+		newFileDir.append(saveDir)
+			.append(File.separator).append(dir).append(File.separator).append(DateUtil.format(DateUtil.now(), "yyyyMMdd"))
+			.append(File.separator);
+		return newFileDir.toString();
+	}
+
+
+	/**
+	 * 图片压缩
+	 *
+	 * @param path 文件地址
+	 */
+	@Override
+	public void compress(String path) {
+		try {
+			ImageUtil.zoomScale(ImageUtil.readImage(path), new FileOutputStream(new File(path)), null, getBladeFileProperties().getCompressScale(), getBladeFileProperties().getCompressFlag());
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		}
+	}
+
+}

+ 76 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/props/BladeFileProperties.java

@@ -0,0 +1,76 @@
+package org.springblade.core.boot.props;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * BladeFileProperties
+ *
+ * @author Chill
+ */
+@Getter
+@Setter
+@ConfigurationProperties("blade.file")
+public class BladeFileProperties {
+
+	/**
+	 * 远程上传模式
+	 */
+	private boolean remoteMode = false;
+
+	/**
+	 * 外网地址
+	 */
+	private String uploadDomain = "http://127.0.0.1:8999";
+
+	/**
+	 * 上传下载路径(物理路径)
+	 */
+	private String remotePath = System.getProperty("user.dir") + "/target/blade";
+
+	/**
+	 * 上传路径(相对路径)
+	 */
+	private String uploadPath = "/upload";
+
+	/**
+	 * 下载路径
+	 */
+	private String downloadPath = "/download";
+
+	/**
+	 * 图片压缩
+	 */
+	private Boolean compress = false;
+
+	/**
+	 * 图片压缩比例
+	 */
+	private Double compressScale = 2.00;
+
+	/**
+	 * 图片缩放选择:true放大;false缩小
+	 */
+	private Boolean compressFlag = false;
+
+	/**
+	 * 项目物理路径
+	 */
+	private String realPath = System.getProperty("user.dir");
+
+	/**
+	 * 项目相对路径
+	 */
+	private String contextPath = "/";
+
+
+	public String getUploadRealPath() {
+		return (remoteMode ? remotePath : realPath) + uploadPath;
+	}
+
+	public String getUploadCtxPath() {
+		return contextPath + uploadPath;
+	}
+
+}

+ 43 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/props/BladeUploadProperties.java

@@ -0,0 +1,43 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.boot.props;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springblade.core.tool.utils.PathUtil;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.lang.Nullable;
+
+
+/**
+ * 文件上传配置
+ *
+ * @author Chill
+ */
+@Getter
+@Setter
+@RefreshScope
+@ConfigurationProperties("blade.upload")
+public class BladeUploadProperties {
+
+	/**
+	 * 文件保存目录,默认:jar 包同级目录
+	 */
+	@Nullable
+	private String savePath = PathUtil.getJarPath();
+}

+ 120 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/request/BladeHttpServletRequestWrapper.java

@@ -0,0 +1,120 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.boot.request;
+
+import org.springblade.core.tool.utils.WebUtil;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+/**
+ * 全局Request包装
+ *
+ * @author Chill
+ */
+public class BladeHttpServletRequestWrapper extends HttpServletRequestWrapper {
+
+	/**
+	 * 没被包装过的HttpServletRequest(特殊场景,需要自己过滤)
+	 */
+	private final HttpServletRequest orgRequest;
+	/**
+	 * 缓存报文,支持多次读取流
+	 */
+	private byte[] body;
+
+
+	public BladeHttpServletRequestWrapper(HttpServletRequest request) {
+		super(request);
+		orgRequest = request;
+	}
+
+	@Override
+	public BufferedReader getReader() throws IOException {
+		return new BufferedReader(new InputStreamReader(getInputStream()));
+	}
+
+	@Override
+	public ServletInputStream getInputStream() throws IOException {
+		if (super.getHeader(HttpHeaders.CONTENT_TYPE) == null) {
+			return super.getInputStream();
+		}
+
+		if (super.getHeader(HttpHeaders.CONTENT_TYPE).startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)) {
+			return super.getInputStream();
+		}
+
+		if (body == null) {
+			body = WebUtil.getRequestBody(super.getInputStream()).getBytes();
+		}
+
+		final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
+
+		return new ServletInputStream() {
+
+			@Override
+			public int read() {
+				return byteArrayInputStream.read();
+			}
+
+			@Override
+			public boolean isFinished() {
+				return false;
+			}
+
+			@Override
+			public boolean isReady() {
+				return false;
+			}
+
+			@Override
+			public void setReadListener(ReadListener readListener) {
+			}
+		};
+	}
+
+	/**
+	 * 获取初始request
+	 *
+	 * @return HttpServletRequest
+	 */
+	public HttpServletRequest getOrgRequest() {
+		return orgRequest;
+	}
+
+	/**
+	 * 获取初始request
+	 *
+	 * @param request request
+	 * @return HttpServletRequest
+	 */
+	public static HttpServletRequest getOrgRequest(HttpServletRequest request) {
+		if (request instanceof BladeHttpServletRequestWrapper) {
+			return ((BladeHttpServletRequestWrapper) request).getOrgRequest();
+		}
+		return request;
+	}
+
+}

+ 75 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/request/BladeRequestFilter.java

@@ -0,0 +1,75 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.boot.request;
+
+import lombok.AllArgsConstructor;
+import org.springframework.util.AntPathMatcher;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+/**
+ * Request全局过滤
+ *
+ * @author Chill
+ */
+@AllArgsConstructor
+public class BladeRequestFilter implements Filter {
+
+	private final RequestProperties requestProperties;
+	private final XssProperties xssProperties;
+	private final AntPathMatcher antPathMatcher = new AntPathMatcher();
+
+	@Override
+	public void init(FilterConfig config) {
+
+	}
+
+	@Override
+	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+		String path = ((HttpServletRequest) request).getServletPath();
+		// 跳过 Request 包装
+		if (!requestProperties.getEnabled() || isRequestSkip(path)) {
+			chain.doFilter(request, response);
+		}
+		// 默认 Request 包装
+		else if (!xssProperties.getEnabled() || isXssSkip(path)) {
+			BladeHttpServletRequestWrapper bladeRequest = new BladeHttpServletRequestWrapper((HttpServletRequest) request);
+			chain.doFilter(bladeRequest, response);
+		}
+		// Xss Request 包装
+		else {
+			XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request);
+			chain.doFilter(xssRequest, response);
+		}
+	}
+
+	private boolean isRequestSkip(String path) {
+		return requestProperties.getSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path));
+	}
+
+	private boolean isXssSkip(String path) {
+		return xssProperties.getSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path));
+	}
+
+	@Override
+	public void destroy() {
+
+	}
+
+}

+ 44 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/request/RequestProperties.java

@@ -0,0 +1,44 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.boot.request;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Request配置类
+ *
+ * @author Chill
+ */
+@Data
+@ConfigurationProperties("blade.request")
+public class RequestProperties {
+
+	/**
+	 * 开启自定义request
+	 */
+	private Boolean enabled = true;
+
+	/**
+	 * 放行url
+	 */
+	private List<String> skipUrl = new ArrayList<>();
+
+}

+ 536 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/request/XssHtmlFilter.java

@@ -0,0 +1,536 @@
+package org.springblade.core.boot.request;
+
+import org.springblade.core.tool.utils.StringPool;
+
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * HTML filtering utility for protecting against XSS (Cross Site Scripting).
+ * <p>
+ * This code is licensed LGPLv3
+ * <p>
+ * This code is a Java port of the original work in PHP by Cal Hendersen.
+ * http://code.iamcal.com/php/lib_filter/
+ * <p>
+ * The trickiest part of the translation was handling the differences in regex handling
+ * between PHP and Java.  These resources were helpful in the process:
+ * <p>
+ * http://java.sun.com/j2se/1.4.2/docs/api/java/util/regex/Pattern.html
+ * http://us2.php.net/manual/en/reference.pcre.pattern.modifiers.php
+ * http://www.regular-expressions.info/modifiers.html
+ * <p>
+ * A note on naming conventions: instance variables are prefixed with a "v"; global
+ * constants are in all caps.
+ * <p>
+ * Sample use:
+ * String input = ...
+ * String clean = new HtmlFilter().filter( input );
+ * <p>
+ * The class is not thread safe. Create a new instance if in doubt.
+ * <p>
+ * If you find bugs or have suggestions on improvement (especially regarding
+ * performance), please contact us.  The latest version of this
+ * source, and our contact details, can be found at http://xss-html-filter.sf.net
+ *
+ * @author Joseph O'Connell
+ * @author Cal Hendersen
+ * @author Michael Semb Wever
+ */
+public final class XssHtmlFilter {
+
+	/**
+	 * regex flag union representing /si modifiers in php
+	 **/
+	private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL;
+	private static final Pattern P_COMMENTS = Pattern.compile("<!--(.*?)-->", Pattern.DOTALL);
+	private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI);
+	private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL);
+	private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI);
+	private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI);
+	private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI);
+	private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI);
+	private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI);
+	private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?");
+	private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?");
+	private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?");
+	private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))");
+	private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL);
+	private static final Pattern P_END_ARROW = Pattern.compile("^>");
+	private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)");
+	private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)");
+	private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)");
+	private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)");
+	private static final Pattern P_AMP = Pattern.compile("&");
+	private static final Pattern P_QUOTE = Pattern.compile("<");
+	private static final Pattern P_LEFT_ARROW = Pattern.compile("<");
+	private static final Pattern P_RIGHT_ARROW = Pattern.compile(">");
+	private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>");
+
+
+	private static final ConcurrentMap<String, Pattern> P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<String, Pattern>();
+	private static final ConcurrentMap<String, Pattern> P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<String, Pattern>();
+
+	/**
+	 * set of allowed html elements, along with allowed attributes for each element
+	 **/
+	private final Map<String, List<String>> vAllowed;
+	/**
+	 * counts of open tags for each (allowable) html element
+	 **/
+	private final Map<String, Integer> vTagCounts = new HashMap<String, Integer>();
+
+	/**
+	 * html elements which must always be self-closing (e.g. "<img />")
+	 **/
+	private final String[] vSelfClosingTags;
+	/**
+	 * html elements which must always have separate opening and closing tags (e.g. "<b></b>")
+	 **/
+	private final String[] vNeedClosingTags;
+	/**
+	 * set of disallowed html elements
+	 **/
+	private final String[] vDisallowed;
+	/**
+	 * attributes which should be checked for valid protocols
+	 **/
+	private final String[] vProtocolAtts;
+	/**
+	 * allowed protocols
+	 **/
+	private final String[] vAllowedProtocols;
+	/**
+	 * tags which should be removed if they contain no content (e.g. "<b></b>" or "<b />")
+	 **/
+	private final String[] vRemoveBlanks;
+	/**
+	 * entities allowed within html markup
+	 **/
+	private final String[] vAllowedEntities;
+	/**
+	 * flag determining whether comments are allowed in input String.
+	 */
+	private final boolean stripComment;
+	private final boolean encodeQuotes;
+	private boolean vDebug = false;
+	/**
+	 * flag determining whether to try to make tags when presented with "unbalanced"
+	 * angle brackets (e.g. "<b text </b>" becomes "<b> text </b>").  If set to false,
+	 * unbalanced angle brackets will be html escaped.
+	 */
+	private final boolean alwaysMakeTags;
+
+	/**
+	 * Default constructor.
+	 */
+	public XssHtmlFilter() {
+		vAllowed = new HashMap<>();
+
+		final ArrayList<String> aAtts = new ArrayList<String>();
+		aAtts.add("href");
+		aAtts.add("target");
+		vAllowed.put("a", aAtts);
+
+		final ArrayList<String> imgAtts = new ArrayList<String>();
+		imgAtts.add("src");
+		imgAtts.add("width");
+		imgAtts.add("height");
+		imgAtts.add("alt");
+		vAllowed.put("img", imgAtts);
+
+		final ArrayList<String> noAtts = new ArrayList<String>();
+		vAllowed.put("b", noAtts);
+		vAllowed.put("strong", noAtts);
+		vAllowed.put("i", noAtts);
+		vAllowed.put("em", noAtts);
+
+		vSelfClosingTags = new String[]{"img"};
+		vNeedClosingTags = new String[]{"a", "b", "strong", "i", "em"};
+		vDisallowed = new String[]{};
+		vAllowedProtocols = new String[]{"http", "mailto", "https"};
+		vProtocolAtts = new String[]{"src", "href"};
+		vRemoveBlanks = new String[]{"a", "b", "strong", "i", "em"};
+		vAllowedEntities = new String[]{"amp", "gt", "lt", "quot"};
+		stripComment = true;
+		encodeQuotes = true;
+		alwaysMakeTags = false;
+	}
+
+	/**
+	 * Set debug flag to true. Otherwise use default settings. See the default constructor.
+	 *
+	 * @param debug turn debug on with a true argument
+	 */
+	public XssHtmlFilter(final boolean debug) {
+		this();
+		vDebug = debug;
+
+	}
+
+	/**
+	 * Map-parameter configurable constructor.
+	 *
+	 * @param conf map containing configuration. keys match field names.
+	 */
+	public XssHtmlFilter(final Map<String, Object> conf) {
+
+		assert conf.containsKey("vAllowed") : "configuration requires vAllowed";
+		assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags";
+		assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags";
+		assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed";
+		assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols";
+		assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts";
+		assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks";
+		assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities";
+
+		vAllowed = Collections.unmodifiableMap((HashMap<String, List<String>>) conf.get("vAllowed"));
+		vSelfClosingTags = (String[]) conf.get("vSelfClosingTags");
+		vNeedClosingTags = (String[]) conf.get("vNeedClosingTags");
+		vDisallowed = (String[]) conf.get("vDisallowed");
+		vAllowedProtocols = (String[]) conf.get("vAllowedProtocols");
+		vProtocolAtts = (String[]) conf.get("vProtocolAtts");
+		vRemoveBlanks = (String[]) conf.get("vRemoveBlanks");
+		vAllowedEntities = (String[]) conf.get("vAllowedEntities");
+		stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true;
+		encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true;
+		alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true;
+	}
+
+	private void reset() {
+		vTagCounts.clear();
+	}
+
+	private void debug(final String msg) {
+		if (vDebug) {
+			Logger.getAnonymousLogger().info(msg);
+		}
+	}
+
+	public static String chr(final int decimal) {
+		return String.valueOf((char) decimal);
+	}
+
+	public static String htmlSpecialChars(final String s) {
+		String result = s;
+		result = regexReplace(P_AMP, "&amp;", result);
+		result = regexReplace(P_QUOTE, "&quot;", result);
+		result = regexReplace(P_LEFT_ARROW, "&lt;", result);
+		result = regexReplace(P_RIGHT_ARROW, "&gt;", result);
+		return result;
+	}
+
+	//---------------------------------------------------------------
+
+	/**
+	 * given a user submitted input String, filter out any invalid or restricted
+	 * html.
+	 *
+	 * @param input text (i.e. submitted by a user) than may contain html
+	 * @return "clean" version of input, with only valid, whitelisted html elements allowed
+	 */
+	public String filter(final String input) {
+		reset();
+		String s = input;
+
+		debug("************************************************");
+		debug("              INPUT: " + input);
+
+		s = escapeComments(s);
+		debug("     escapeComments: " + s);
+
+		s = balanceHtml(s);
+		debug("        balanceHtml: " + s);
+
+		s = checkTags(s);
+		debug("          checkTags: " + s);
+
+		s = processRemoveBlanks(s);
+		debug("processRemoveBlanks: " + s);
+
+		s = validateEntities(s);
+		debug("    validateEntites: " + s);
+
+		debug("************************************************\n\n");
+		return s;
+	}
+
+	public boolean isAlwaysMakeTags() {
+		return alwaysMakeTags;
+	}
+
+	public boolean isStripComments() {
+		return stripComment;
+	}
+
+	private String escapeComments(final String s) {
+		final Matcher m = P_COMMENTS.matcher(s);
+		final StringBuffer buf = new StringBuffer();
+		if (m.find()) {
+			final String match = m.group(1);
+			m.appendReplacement(buf, Matcher.quoteReplacement("<!--" + htmlSpecialChars(match) + "-->"));
+		}
+		m.appendTail(buf);
+
+		return buf.toString();
+	}
+
+	private String balanceHtml(String s) {
+		if (alwaysMakeTags) {
+			//
+			// try and form html
+			//
+			s = regexReplace(P_END_ARROW, "", s);
+			s = regexReplace(P_BODY_TO_END, "<$1>", s);
+			s = regexReplace(P_XML_CONTENT, "$1<$2", s);
+
+		} else {
+			//
+			// escape stray brackets
+			//
+			s = regexReplace(P_STRAY_LEFT_ARROW, "&lt;$1", s);
+			s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2&gt;<", s);
+
+			//
+			// the last regexp causes '<>' entities to appear
+			// (we need to do a lookahead assertion so that the last bracket can
+			// be used in the next pass of the regexp)
+			//
+			s = regexReplace(P_BOTH_ARROWS, "", s);
+		}
+
+		return s;
+	}
+
+	private String checkTags(String s) {
+		Matcher m = P_TAGS.matcher(s);
+
+		final StringBuffer buf = new StringBuffer();
+		while (m.find()) {
+			String replaceStr = m.group(1);
+			replaceStr = processTag(replaceStr);
+			m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr));
+		}
+		m.appendTail(buf);
+
+		s = buf.toString();
+
+		// these get tallied in processTag
+		// (remember to reset before subsequent calls to filter method)
+		for (String key : vTagCounts.keySet()) {
+			for (int ii = 0; ii < vTagCounts.get(key); ii++) {
+				s += "</" + key + ">";
+			}
+		}
+
+		return s;
+	}
+
+	private String processRemoveBlanks(final String s) {
+		String result = s;
+		for (String tag : vRemoveBlanks) {
+			if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) {
+				P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?></" + tag + ">"));
+			}
+			result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result);
+			if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) {
+				P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>"));
+			}
+			result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result);
+		}
+
+		return result;
+	}
+
+	private static String regexReplace(final Pattern regexPattern, final String replacement, final String s) {
+		Matcher m = regexPattern.matcher(s);
+		return m.replaceAll(replacement);
+	}
+
+	private String processTag(final String s) {
+		Matcher m = P_END_TAG.matcher(s);
+		if (m.find()) {
+			final String name = m.group(1).toLowerCase();
+			if (allowed(name)) {
+				if (!inArray(name, vSelfClosingTags)) {
+					if (vTagCounts.containsKey(name)) {
+						vTagCounts.put(name, vTagCounts.get(name) - 1);
+						return "</" + name + ">";
+					}
+				}
+			}
+		}
+		m = P_START_TAG.matcher(s);
+		if (m.find()) {
+			final String name = m.group(1).toLowerCase();
+			final String body = m.group(2);
+			String ending = m.group(3);
+			if (allowed(name)) {
+				String params = "";
+				final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body);
+				final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body);
+				final List<String> paramNames = new ArrayList<String>();
+				final List<String> paramValues = new ArrayList<String>();
+				while (m2.find()) {
+					paramNames.add(m2.group(1));
+					paramValues.add(m2.group(3));
+				}
+				while (m3.find()) {
+					paramNames.add(m3.group(1));
+					paramValues.add(m3.group(3));
+				}
+				String paramName, paramValue;
+				for (int ii = 0; ii < paramNames.size(); ii++) {
+					paramName = paramNames.get(ii).toLowerCase();
+					paramValue = paramValues.get(ii);
+					if (allowedAttribute(name, paramName)) {
+						if (inArray(paramName, vProtocolAtts)) {
+							paramValue = processParamProtocol(paramValue);
+						}
+						params += " " + paramName + "=\"" + paramValue + "\"";
+					}
+				}
+				if (inArray(name, vSelfClosingTags)) {
+					ending = " /";
+				}
+				if (inArray(name, vNeedClosingTags)) {
+					ending = "";
+				}
+				if (ending == null || ending.length() < 1) {
+					if (vTagCounts.containsKey(name)) {
+						vTagCounts.put(name, vTagCounts.get(name) + 1);
+					} else {
+						vTagCounts.put(name, 1);
+					}
+				} else {
+					ending = " /";
+				}
+				return "<" + name + params + ending + ">";
+			} else {
+				return "";
+			}
+		}
+		m = P_COMMENT.matcher(s);
+		if (!stripComment && m.find()) {
+			return "<" + m.group() + ">";
+		}
+		return "";
+	}
+
+	private String processParamProtocol(String s) {
+		s = decodeEntities(s);
+		final Matcher m = P_PROTOCOL.matcher(s);
+		if (m.find()) {
+			final String protocol = m.group(1);
+			if (!inArray(protocol, vAllowedProtocols)) {
+				// bad protocol, turn into local anchor link instead
+				s = "#" + s.substring(protocol.length() + 1);
+				if (s.startsWith(StringPool.DOUBLE_SLASH)) {
+					s = "#" + s.substring(3);
+				}
+			}
+		}
+
+		return s;
+	}
+
+	private String decodeEntities(String s) {
+		StringBuffer buf = new StringBuffer();
+
+		Matcher m = P_ENTITY.matcher(s);
+		while (m.find()) {
+			final String match = m.group(1);
+			final int decimal = Integer.decode(match);
+			m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+		}
+		m.appendTail(buf);
+		s = buf.toString();
+
+		buf = new StringBuffer();
+		m = P_ENTITY_UNICODE.matcher(s);
+		while (m.find()) {
+			final String match = m.group(1);
+			final int decimal = Integer.valueOf(match, 16).intValue();
+			m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+		}
+		m.appendTail(buf);
+		s = buf.toString();
+
+		buf = new StringBuffer();
+		m = P_ENCODE.matcher(s);
+		while (m.find()) {
+			final String match = m.group(1);
+			final int decimal = Integer.valueOf(match, 16).intValue();
+			m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal)));
+		}
+		m.appendTail(buf);
+		s = buf.toString();
+
+		s = validateEntities(s);
+		return s;
+	}
+
+	private String validateEntities(final String s) {
+		StringBuffer buf = new StringBuffer();
+
+		// validate entities throughout the string
+		Matcher m = P_VALID_ENTITIES.matcher(s);
+		while (m.find()) {
+			final String one = m.group(1);
+			final String two = m.group(2);
+			m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two)));
+		}
+		m.appendTail(buf);
+
+		return encodeQuotes(buf.toString());
+	}
+
+	private String encodeQuotes(final String s) {
+		if (encodeQuotes) {
+			StringBuffer buf = new StringBuffer();
+			Matcher m = P_VALID_QUOTES.matcher(s);
+			while (m.find()) {
+				final String one = m.group(1);
+				final String two = m.group(2);
+				final String three = m.group(3);
+				m.appendReplacement(buf, Matcher.quoteReplacement(one + regexReplace(P_QUOTE, "&quot;", two) + three));
+			}
+			m.appendTail(buf);
+			return buf.toString();
+		} else {
+			return s;
+		}
+	}
+
+	private String checkEntity(final String preamble, final String term) {
+
+		return ";".equals(term) && isValidEntity(preamble)
+			? '&' + preamble
+			: "&amp;" + preamble;
+	}
+
+	private boolean isValidEntity(final String entity) {
+		return inArray(entity, vAllowedEntities);
+	}
+
+	private static boolean inArray(final String s, final String[] array) {
+		for (String item : array) {
+			if (item != null && item.equals(s)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	private boolean allowed(final String name) {
+		return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed);
+	}
+
+	private boolean allowedAttribute(final String name, final String paramName) {
+		return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName));
+	}
+}

+ 175 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/request/XssHttpServletRequestWrapper.java

@@ -0,0 +1,175 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.boot.request;
+
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.core.tool.utils.WebUtil;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+
+import javax.servlet.ReadListener;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * XSS过滤
+ *
+ * @author Chill
+ */
+public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
+
+	/**
+	 * 没被包装过的HttpServletRequest(特殊场景,需要自己过滤)
+	 */
+	private final HttpServletRequest orgRequest;
+	/**
+	 * 缓存报文,支持多次读取流
+	 */
+	private byte[] body;
+	/**
+	 * html过滤
+	 */
+	private final static XssHtmlFilter HTML_FILTER = new XssHtmlFilter();
+
+	public XssHttpServletRequestWrapper(HttpServletRequest request) {
+		super(request);
+		orgRequest = request;
+	}
+
+	@Override
+	public BufferedReader getReader() throws IOException {
+		return new BufferedReader(new InputStreamReader(getInputStream()));
+	}
+
+	@Override
+	public ServletInputStream getInputStream() throws IOException {
+		if (super.getHeader(HttpHeaders.CONTENT_TYPE) == null) {
+			return super.getInputStream();
+		}
+
+		if (super.getHeader(HttpHeaders.CONTENT_TYPE).startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)) {
+			return super.getInputStream();
+		}
+
+		if (body == null) {
+			body = xssEncode(WebUtil.getRequestBody(super.getInputStream())).getBytes();
+		}
+
+		final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);
+
+		return new ServletInputStream() {
+
+			@Override
+			public int read() {
+				return byteArrayInputStream.read();
+			}
+
+			@Override
+			public boolean isFinished() {
+				return false;
+			}
+
+			@Override
+			public boolean isReady() {
+				return false;
+			}
+
+			@Override
+			public void setReadListener(ReadListener readListener) {
+			}
+		};
+	}
+
+	@Override
+	public String getParameter(String name) {
+		String value = super.getParameter(xssEncode(name));
+		if (StringUtil.isNotBlank(value)) {
+			value = xssEncode(value);
+		}
+		return value;
+	}
+
+	@Override
+	public String[] getParameterValues(String name) {
+		String[] parameters = super.getParameterValues(name);
+		if (parameters == null || parameters.length == 0) {
+			return null;
+		}
+
+		for (int i = 0; i < parameters.length; i++) {
+			parameters[i] = xssEncode(parameters[i]);
+		}
+		return parameters;
+	}
+
+	@Override
+	public Map<String, String[]> getParameterMap() {
+		Map<String, String[]> map = new LinkedHashMap<>();
+		Map<String, String[]> parameters = super.getParameterMap();
+		for (String key : parameters.keySet()) {
+			String[] values = parameters.get(key);
+			for (int i = 0; i < values.length; i++) {
+				values[i] = xssEncode(values[i]);
+			}
+			map.put(key, values);
+		}
+		return map;
+	}
+
+	@Override
+	public String getHeader(String name) {
+		String value = super.getHeader(xssEncode(name));
+		if (StringUtil.isNotBlank(value)) {
+			value = xssEncode(value);
+		}
+		return value;
+	}
+
+	private String xssEncode(String input) {
+		return HTML_FILTER.filter(input);
+	}
+
+	/**
+	 * 获取初始request
+	 *
+	 * @return HttpServletRequest
+	 */
+	public HttpServletRequest getOrgRequest() {
+		return orgRequest;
+	}
+
+	/**
+	 * 获取初始request
+	 *
+	 * @param request request
+	 * @return HttpServletRequest
+	 */
+	public static HttpServletRequest getOrgRequest(HttpServletRequest request) {
+		if (request instanceof XssHttpServletRequestWrapper) {
+			return ((XssHttpServletRequestWrapper) request).getOrgRequest();
+		}
+		return request;
+	}
+
+}

+ 44 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/request/XssProperties.java

@@ -0,0 +1,44 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.boot.request;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Xss配置类
+ *
+ * @author Chill
+ */
+@Data
+@ConfigurationProperties("blade.xss")
+public class XssProperties {
+
+	/**
+	 * 开启xss
+	 */
+	private Boolean enabled = true;
+
+	/**
+	 * 放行url
+	 */
+	private List<String> skipUrl = new ArrayList<>();
+
+}

+ 64 - 0
bladex-tool/blade-core-boot/src/main/java/org/springblade/core/boot/resolver/TokenArgumentResolver.java

@@ -0,0 +1,64 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.boot.resolver;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.core.secure.BladeUser;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springframework.core.MethodParameter;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+
+/**
+ * Token转化BladeUser
+ *
+ * @author Chill
+ */
+@Slf4j
+public class TokenArgumentResolver implements HandlerMethodArgumentResolver {
+
+	/**
+	 * 入参筛选
+	 *
+	 * @param methodParameter 参数集合
+	 * @return 格式化后的参数
+	 */
+	@Override
+	public boolean supportsParameter(MethodParameter methodParameter) {
+		return methodParameter.getParameterType().equals(BladeUser.class);
+	}
+
+	/**
+	 * 出参设置
+	 *
+	 * @param methodParameter       入参集合
+	 * @param modelAndViewContainer model 和 view
+	 * @param nativeWebRequest      web相关
+	 * @param webDataBinderFactory  入参解析
+	 * @return 包装对象
+	 */
+	@Override
+	public Object resolveArgument(MethodParameter methodParameter,
+								  ModelAndViewContainer modelAndViewContainer,
+								  NativeWebRequest nativeWebRequest,
+								  WebDataBinderFactory webDataBinderFactory) {
+		return AuthUtil.getUser();
+	}
+
+}

+ 8 - 0
bladex-tool/blade-core-boot/src/main/resources/banner.txt

@@ -0,0 +1,8 @@
+${AnsiColor.BLUE}                   ______  _             _       ___   ___
+${AnsiColor.BLUE}                   | ___ \| |           | |      \  \ /  /
+${AnsiColor.BLUE}                   | |_/ /| |  __ _   __| |  ___  \  V  /
+${AnsiColor.BLUE}                   | ___ \| | / _` | / _` | / _ \   > <
+${AnsiColor.BLUE}                   | |_/ /| || (_| || (_| ||  __/ /  .  \
+${AnsiColor.BLUE}                   \____/ |_| \__,_| \__,_| \___|/__/ \__\
+
+${AnsiColor.BLUE}:: BladeX ${blade.service.version} :: ${spring.application.name}:${AnsiColor.RED}${blade.env}${AnsiColor.BLUE} :: Running SpringBoot ${spring-boot.version} :: ${AnsiColor.BRIGHT_BLACK}

+ 31 - 0
bladex-tool/blade-core-boot/src/main/resources/blade-boot.yml

@@ -0,0 +1,31 @@
+#服务器配置
+server:
+  undertow:
+    threads:
+      # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
+      io: 16
+      # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
+      worker: 400
+    # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
+    buffer-size: 1024
+    # 是否分配的直接内存
+    direct-buffers: true
+
+#spring配置
+spring:
+  http:
+    encoding:
+      charset: UTF-8
+      force: true
+  servlet:
+    multipart:
+      max-file-size: 256MB
+      max-request-size: 1024MB
+  mvc:
+    throw-exception-if-no-handler-found: true
+  resources:
+    add-mappings: false
+  devtools:
+    restart:
+      log-condition-evaluation-delta: false
+

BIN
bladex-tool/blade-core-boot/src/main/resources/static/favicon.ico


+ 102 - 0
bladex-tool/blade-core-cloud/pom.xml

@@ -0,0 +1,102 @@
+<?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>BladeX-Tool</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.8.2.RELEASE</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-core-cloud</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${project.parent.version}</version>
+    <packaging>jar</packaging>
+
+
+    <dependencies>
+        <!--Blade-->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-context</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-auth</artifactId>
+        </dependency>
+        <!--Feign-->
+        <dependency>
+            <groupId>io.github.openfeign</groupId>
+            <artifactId>feign-okhttp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <!--Hystrix-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <!-- Actuator -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-actuator</artifactId>
+        </dependency>
+        <!-- Admin -->
+        <dependency>
+            <groupId>de.codecentric</groupId>
+            <artifactId>spring-boot-admin-starter-client</artifactId>
+        </dependency>
+        <!-- Nacos -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.alibaba.nacos</groupId>
+                    <artifactId>nacos-client</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.alibaba.nacos</groupId>
+                    <artifactId>nacos-client</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.nacos</groupId>
+            <artifactId>nacos-client</artifactId>
+        </dependency>
+        <!-- Sentinel -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
+        </dependency>
+        <!-- Auto -->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-auto</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>

+ 39 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/annotation/ApiVersion.java

@@ -0,0 +1,39 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.cloud.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * header 版本 处理
+ *
+ * @author L.cm
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface ApiVersion {
+
+	/**
+	 * header 路径中的版本
+	 *
+	 * @return 版本号
+	 */
+	String value() default "";
+
+}

+ 38 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/annotation/UrlVersion.java

@@ -0,0 +1,38 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.cloud.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * 注解用于生成 requestMappingInfo 时候直接拼接路径规则,自动放置于方法路径开始部分
+ *
+ * @author L.cm
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface UrlVersion {
+
+	/**
+	 * url 路径中的版本
+	 *
+	 * @return 版本号
+	 */
+	String value() default "";
+}

+ 107 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/annotation/VersionMapping.java

@@ -0,0 +1,107 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.cloud.annotation;
+
+import org.springframework.core.annotation.AliasFor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import java.lang.annotation.*;
+
+/**
+ * 版本号处理
+ *
+ * <p>
+ *     1. url 版本号:添加到 url 前
+ *     2. Accept 版本:application/vnd.blade.VERSION+json
+ * </p>
+ *
+ * @author L.cm
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@RequestMapping
+@UrlVersion
+@ApiVersion
+@Validated
+public @interface VersionMapping {
+	/**
+	 * Alias for {@link RequestMapping#name}.
+	 * @return {String[]}
+	 */
+	@AliasFor(annotation = RequestMapping.class)
+	String name() default "";
+
+	/**
+	 * Alias for {@link RequestMapping#value}.
+	 * @return {String[]}
+	 */
+	@AliasFor(annotation = RequestMapping.class)
+	String[] value() default {};
+
+	/**
+	 * Alias for {@link RequestMapping#path}.
+	 * @return {String[]}
+	 */
+	@AliasFor(annotation = RequestMapping.class)
+	String[] path() default {};
+
+	/**
+	 * Alias for {@link RequestMapping#params}.
+	 * @return {String[]}
+	 */
+	@AliasFor(annotation = RequestMapping.class)
+	String[] params() default {};
+
+	/**
+	 * Alias for {@link RequestMapping#headers}.
+	 * @return {String[]}
+	 */
+	@AliasFor(annotation = RequestMapping.class)
+	String[] headers() default {};
+
+	/**
+	 * Alias for {@link RequestMapping#consumes}.
+	 * @return {String[]}
+	 */
+	@AliasFor(annotation = RequestMapping.class)
+	String[] consumes() default {};
+
+	/**
+	 * Alias for {@link RequestMapping#produces}.
+	 * default json utf-8
+	 * @return {String[]}
+	 */
+	@AliasFor(annotation = RequestMapping.class)
+	String[] produces() default {};
+
+	/**
+	 * Alias for {@link UrlVersion#value}.
+	 * @return {String}
+	 */
+	@AliasFor(annotation = UrlVersion.class, attribute = "value")
+	String urlVersion() default "";
+
+	/**
+	 * Alias for {@link ApiVersion#value}.
+	 * @return {String}
+	 */
+	@AliasFor(annotation = ApiVersion.class, attribute = "value")
+	String apiVersion() default "";
+
+}

+ 40 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/client/BladeCloudApplication.java

@@ -0,0 +1,40 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.cloud.client;
+
+import org.springblade.core.launch.constant.AppConstant;
+import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+
+import java.lang.annotation.*;
+
+/**
+ * Cloud启动注解配置
+ *
+ * @author Chill
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+@EnableDiscoveryClient
+@EnableCircuitBreaker
+@EnableFeignClients(AppConstant.BASE_PACKAGES)
+public @interface BladeCloudApplication {
+
+}

+ 45 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/feign/BladeFallbackFactory.java

@@ -0,0 +1,45 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.cloud.feign;
+
+import feign.Target;
+import feign.hystrix.FallbackFactory;
+import lombok.AllArgsConstructor;
+import org.springframework.cglib.proxy.Enhancer;
+
+/**
+ * 默认 Fallback,避免写过多fallback类
+ *
+ * @param <T> 泛型标记
+ * @author L.cm
+ */
+@AllArgsConstructor
+public class BladeFallbackFactory<T> implements FallbackFactory<T> {
+	private final Target<T> target;
+
+	@Override
+	@SuppressWarnings("unchecked")
+	public T create(Throwable cause) {
+		final Class<T> targetType = target.type();
+		final String targetName = target.name();
+		Enhancer enhancer = new Enhancer();
+		enhancer.setSuperclass(targetType);
+		enhancer.setUseCache(true);
+		enhancer.setCallback(new BladeFeignFallback<>(targetType, targetName, cause));
+		return (T) enhancer.create();
+	}
+}

+ 102 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/feign/BladeFeignFallback.java

@@ -0,0 +1,102 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.cloud.feign;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import feign.FeignException;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.api.ResultCode;
+import org.springblade.core.tool.jackson.JsonUtil;
+import org.springblade.core.tool.utils.ObjectUtil;
+import org.springframework.cglib.proxy.MethodInterceptor;
+import org.springframework.cglib.proxy.MethodProxy;
+import org.springframework.lang.Nullable;
+
+import java.lang.reflect.Method;
+import java.util.*;
+
+/**
+ * blade fallBack 代理处理
+ *
+ * @author L.cm
+ */
+@Slf4j
+@AllArgsConstructor
+public class BladeFeignFallback<T> implements MethodInterceptor {
+	private final Class<T> targetType;
+	private final String targetName;
+	private final Throwable cause;
+	private final static String CODE = "code";
+
+	@Nullable
+	@Override
+	public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
+		String errorMessage = cause.getMessage();
+		log.error("BladeFeignFallback:[{}.{}] serviceId:[{}] message:[{}]", targetType.getName(), method.getName(), targetName, errorMessage);
+		Class<?> returnType = method.getReturnType();
+		// 集合类型反馈空集合
+		if (List.class == returnType || Collection.class == returnType) {
+			return Collections.emptyList();
+		}
+		if (Set.class == returnType) {
+			return Collections.emptySet();
+		}
+		if (Map.class == returnType) {
+			return Collections.emptyMap();
+		}
+		// 暂时不支持 flux,rx,异步等,返回值不是 R,直接返回 null。
+		if (R.class != returnType) {
+			return null;
+		}
+		// 非 FeignException
+		if (!(cause instanceof FeignException)) {
+			return R.fail(ResultCode.INTERNAL_SERVER_ERROR, errorMessage);
+		}
+		FeignException exception = (FeignException) cause;
+		byte[] content = exception.content();
+		// 如果返回的数据为空
+		if (ObjectUtil.isEmpty(content)) {
+			return R.fail(ResultCode.INTERNAL_SERVER_ERROR, errorMessage);
+		}
+		// 转换成 jsonNode 读取,因为直接转换,可能 对方放回的并 不是 R 的格式。
+		JsonNode resultNode = JsonUtil.readTree(content);
+		// 判断是否 R 格式 返回体
+		if (resultNode.has(CODE)) {
+			return JsonUtil.getInstance().convertValue(resultNode, R.class);
+		}
+		return R.fail(resultNode.toString());
+	}
+
+	@Override
+	public boolean equals(Object o) {
+		if (this == o) {
+			return true;
+		}
+		if (o == null || getClass() != o.getClass()) {
+			return false;
+		}
+		BladeFeignFallback<?> that = (BladeFeignFallback<?>) o;
+		return targetType.equals(that.targetType);
+	}
+
+	@Override
+	public int hashCode() {
+		return Objects.hash(targetType);
+	}
+}

+ 48 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/feign/BladeFeignRequestInterceptor.java

@@ -0,0 +1,48 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.cloud.feign;
+
+import feign.RequestInterceptor;
+import feign.RequestTemplate;
+import org.springblade.core.tool.constant.BladeConstant;
+import org.springblade.core.tool.utils.ThreadLocalUtil;
+import org.springframework.http.HttpHeaders;
+
+/**
+ * feign 传递Request header
+ *
+ * <p>
+ *     https://blog.csdn.net/u014519194/article/details/77160958
+ *     http://tietang.wang/2016/02/25/hystrix/Hystrix%E5%8F%82%E6%95%B0%E8%AF%A6%E8%A7%A3/
+ *     https://github.com/Netflix/Hystrix/issues/92#issuecomment-260548068
+ * </p>
+ *
+ * @author L.cm
+ */
+public class BladeFeignRequestInterceptor implements RequestInterceptor {
+
+	@Override
+	public void apply(RequestTemplate requestTemplate) {
+		HttpHeaders headers = ThreadLocalUtil.get(BladeConstant.CONTEXT_KEY);
+		if (headers != null && !headers.isEmpty()) {
+			headers.forEach((key, values) ->
+				values.forEach(value -> requestTemplate.header(key, value))
+			);
+		}
+	}
+
+}

+ 81 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/feign/EnableBladeFeign.java

@@ -0,0 +1,81 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+
+package org.springblade.core.cloud.feign;
+
+
+import org.springblade.core.launch.constant.AppConstant;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+
+import java.lang.annotation.*;
+
+/**
+ * 开启Feign注解
+ *
+ * @author Chill
+ */
+@Documented
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.RUNTIME)
+@EnableFeignClients(AppConstant.BASE_PACKAGES)
+public @interface EnableBladeFeign {
+	/**
+	 * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation
+	 * declarations e.g.: {@code @ComponentScan("org.my.pkg")} instead of
+	 * {@code @ComponentScan(basePackages="org.my.pkg")}.
+	 *
+	 * @return the array of 'basePackages'.
+	 */
+	String[] value() default {};
+
+	/**
+	 * Base packages to scan for annotated components.
+	 * <p>
+	 * {@link #value()} is an alias for (and mutually exclusive with) this attribute.
+	 * <p>
+	 * Use {@link #basePackageClasses()} for a type-safe alternative to String-based
+	 * package names.
+	 *
+	 * @return the array of 'basePackages'.
+	 */
+	String[] basePackages() default {};
+
+	/**
+	 * Type-safe alternative to {@link #basePackages()} for specifying the packages to
+	 * scan for annotated components. The package of each class specified will be scanned.
+	 * <p>
+	 * Consider creating a special no-op marker class or interface in each package that
+	 * serves no purpose other than being referenced by this attribute.
+	 *
+	 * @return the array of 'basePackageClasses'.
+	 */
+	Class<?>[] basePackageClasses() default {};
+
+	/**
+	 * A custom <code>@Configuration</code> for all feign clients. Can contain override
+	 * <code>@Bean</code> definition for the pieces that make up the client, for instance
+	 * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
+	 */
+	Class<?>[] defaultConfiguration() default {};
+
+	/**
+	 * List of classes annotated with @FeignClient. If not empty, disables classpath scanning.
+	 *
+	 * @return
+	 */
+	Class<?>[] clients() default {};
+}

+ 30 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/BladeHttpConfiguration.java

@@ -0,0 +1,30 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.cloud.http;
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * http 配置
+ *
+ * @author L.cm
+ */
+@Configuration(proxyBeanMethods = false)
+@EnableConfigurationProperties(BladeHttpProperties.class)
+public class BladeHttpConfiguration {
+}

+ 65 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/BladeHttpProperties.java

@@ -0,0 +1,65 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.cloud.http;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springblade.core.launch.log.BladeLogLevel;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * http 配置
+ *
+ * @author L.cm
+ */
+@Getter
+@Setter
+@RefreshScope
+@ConfigurationProperties("blade.http")
+public class BladeHttpProperties {
+	/**
+	 * 最大连接数,默认:200
+	 */
+	private int maxConnections = 200;
+	/**
+	 * 连接存活时间,默认:900L
+	 */
+	private long timeToLive = 900L;
+	/**
+	 * 连接池存活时间单位,默认:秒
+	 */
+	private TimeUnit timeUnit = TimeUnit.SECONDS;
+	/**
+	 * 链接超时,默认:2000毫秒
+	 */
+	private int connectionTimeout = 2000;
+	/**
+	 * 是否支持重定向,默认:true
+	 */
+	private boolean followRedirects = true;
+	/**
+	 * 关闭证书校验
+	 */
+	private boolean disableSslValidation = true;
+	/**
+	 * 日志级别
+	 */
+	private BladeLogLevel level = BladeLogLevel.NONE;
+}

+ 27 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/LbRestTemplate.java

@@ -0,0 +1,27 @@
+package org.springblade.core.cloud.http;
+
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.List;
+
+/**
+ * Loadbalancer RestTemplate
+ *
+ * @author L.cm
+ */
+public class LbRestTemplate extends RestTemplate {
+
+	public LbRestTemplate() {
+		super();
+	}
+
+	public LbRestTemplate(ClientHttpRequestFactory requestFactory) {
+		super(requestFactory);
+	}
+
+	public LbRestTemplate(List<HttpMessageConverter<?>> messageConverters) {
+		super(messageConverters);
+	}
+}

+ 191 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/RestTemplateConfiguration.java

@@ -0,0 +1,191 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.cloud.http;
+
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import okhttp3.ConnectionPool;
+import okhttp3.OkHttpClient;
+import org.springblade.core.cloud.http.logger.HttpLoggingInterceptor;
+import org.springblade.core.cloud.http.logger.OkHttpSlf4jLogger;
+import org.springblade.core.tool.ssl.DisableValidationTrustManager;
+import org.springblade.core.tool.ssl.TrustAllHostNames;
+import org.springblade.core.tool.utils.Charsets;
+import org.springblade.core.tool.utils.Holder;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.cloud.client.loadbalancer.LoadBalanced;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.converter.StringHttpMessageConverter;
+import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
+import org.springframework.web.client.RestTemplate;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Http RestTemplateHeaderInterceptor 配置
+ *
+ * @author L.cm
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Configuration(proxyBeanMethods = false)
+@ConditionalOnClass(OkHttpClient.class)
+@ConditionalOnProperty(value = "blade.http.enabled", matchIfMissing = true)
+public class RestTemplateConfiguration {
+	private final BladeHttpProperties properties;
+
+	/**
+	 * okhttp3 请求日志拦截器
+	 *
+	 * @return HttpLoggingInterceptor
+	 */
+	@Bean
+	public HttpLoggingInterceptor loggingInterceptor() {
+		HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new OkHttpSlf4jLogger());
+		interceptor.setLevel(properties.getLevel());
+		return interceptor;
+	}
+
+	/**
+	 * okhttp3 链接池配置
+	 *
+	 * @return okhttp3.ConnectionPool
+	 */
+	@Bean
+	@ConditionalOnMissingBean
+	public ConnectionPool httpClientConnectionPool() {
+		int maxTotalConnections = properties.getMaxConnections();
+		long timeToLive = properties.getTimeToLive();
+		TimeUnit ttlUnit = properties.getTimeUnit();
+		return new ConnectionPool(maxTotalConnections, timeToLive, ttlUnit);
+	}
+
+	/**
+	 * 配置OkHttpClient
+	 *
+	 * @param connectionPool 链接池配置
+	 * @param interceptor    拦截器
+	 * @return OkHttpClient
+	 */
+	@Bean
+	@ConditionalOnMissingBean
+	public OkHttpClient okHttpClient(ConnectionPool connectionPool, HttpLoggingInterceptor interceptor) {
+		boolean followRedirects = properties.isFollowRedirects();
+		int connectTimeout = properties.getConnectionTimeout();
+		return this.createBuilder(properties.isDisableSslValidation())
+			.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
+			.writeTimeout(30, TimeUnit.SECONDS)
+			.readTimeout(30, TimeUnit.SECONDS)
+			.followRedirects(followRedirects)
+			.connectionPool(connectionPool)
+			.addInterceptor(interceptor)
+			.build();
+	}
+
+	private OkHttpClient.Builder createBuilder(boolean disableSslValidation) {
+		OkHttpClient.Builder builder = new OkHttpClient.Builder();
+		if (disableSslValidation) {
+			try {
+				X509TrustManager disabledTrustManager = DisableValidationTrustManager.INSTANCE;
+				TrustManager[] trustManagers = new TrustManager[]{disabledTrustManager};
+				SSLContext sslContext = SSLContext.getInstance("SSL");
+				sslContext.init(null, trustManagers, Holder.SECURE_RANDOM);
+				SSLSocketFactory disabledSslSocketFactory = sslContext.getSocketFactory();
+				builder.sslSocketFactory(disabledSslSocketFactory, disabledTrustManager);
+				builder.hostnameVerifier(TrustAllHostNames.INSTANCE);
+			} catch (NoSuchAlgorithmException | KeyManagementException e) {
+				log.warn("Error setting SSLSocketFactory in OKHttpClient", e);
+			}
+		}
+		return builder;
+	}
+
+	@Bean
+	public RestTemplateHeaderInterceptor requestHeaderInterceptor() {
+		return new RestTemplateHeaderInterceptor();
+	}
+
+	@Configuration(proxyBeanMethods = false)
+	@RequiredArgsConstructor
+	@ConditionalOnClass(OkHttpClient.class)
+	@ConditionalOnProperty(value = "blade.http.rest-template.enable")
+	public static class RestTemplateAutoConfiguration {
+		private final ApplicationContext context;
+
+		/**
+		 * 普通的 RestTemplate,不透传请求头,一般只做外部 http 调用
+		 *
+		 * @param okHttpClient OkHttpClient
+		 * @return RestTemplate
+		 */
+		@Bean
+		@ConditionalOnMissingBean
+		public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder, OkHttpClient okHttpClient) {
+			restTemplateBuilder.requestFactory(() -> new OkHttp3ClientHttpRequestFactory(okHttpClient));
+			RestTemplate restTemplate = restTemplateBuilder.build();
+			configMessageConverters(context, restTemplate.getMessageConverters());
+			return restTemplate;
+		}
+	}
+
+	@Configuration(proxyBeanMethods = false)
+	@RequiredArgsConstructor
+	@ConditionalOnClass(OkHttpClient.class)
+	@ConditionalOnProperty(value = "blade.http.lb-rest-template.enable")
+	public static class LbRestTemplateAutoConfiguration {
+		private final ApplicationContext context;
+
+		/**
+		 * 支持负载均衡的 LbRestTemplate
+		 *
+		 * @param okHttpClient OkHttpClient
+		 * @return LbRestTemplate
+		 */
+		@Bean
+		@LoadBalanced
+		@ConditionalOnMissingBean
+		public LbRestTemplate lbRestTemplate(RestTemplateBuilder restTemplateBuilder, OkHttpClient okHttpClient) {
+			restTemplateBuilder.requestFactory(() -> new OkHttp3ClientHttpRequestFactory(okHttpClient));
+			LbRestTemplate restTemplate = restTemplateBuilder.build(LbRestTemplate.class);
+			restTemplate.getInterceptors().add(context.getBean(RestTemplateHeaderInterceptor.class));
+			configMessageConverters(context, restTemplate.getMessageConverters());
+			return restTemplate;
+		}
+	}
+
+	private static void configMessageConverters(ApplicationContext context, List<HttpMessageConverter<?>> converters) {
+		converters.removeIf(x -> x instanceof StringHttpMessageConverter || x instanceof MappingJackson2HttpMessageConverter);
+		converters.add(new StringHttpMessageConverter(Charsets.UTF_8));
+		converters.add(new MappingJackson2HttpMessageConverter(context.getBean(ObjectMapper.class)));
+	}
+}

+ 48 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/RestTemplateHeaderInterceptor.java

@@ -0,0 +1,48 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.cloud.http;
+
+import lombok.AllArgsConstructor;
+import org.springblade.core.tool.constant.BladeConstant;
+import org.springblade.core.tool.utils.ThreadLocalUtil;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.lang.NonNull;
+
+import java.io.IOException;
+
+/**
+ * RestTemplateHeaderInterceptor 传递Request header
+ *
+ * @author L.cm
+ */
+@AllArgsConstructor
+public class RestTemplateHeaderInterceptor implements ClientHttpRequestInterceptor {
+	@NonNull
+	@Override
+	public ClientHttpResponse intercept(@NonNull HttpRequest request, @NonNull byte[] bytes, @NonNull ClientHttpRequestExecution execution) throws IOException {
+		HttpHeaders headers = ThreadLocalUtil.get(BladeConstant.CONTEXT_KEY);
+		if (headers != null && !headers.isEmpty()) {
+			HttpHeaders httpHeaders = request.getHeaders();
+			headers.forEach((key, values) -> values.forEach(value -> httpHeaders.add(key, value)));
+		}
+		return execution.execute(request, bytes);
+	}
+}

+ 257 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/logger/HttpLoggingInterceptor.java

@@ -0,0 +1,257 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+
+package org.springblade.core.cloud.http.logger;
+
+import okhttp3.*;
+import okhttp3.internal.http.HttpHeaders;
+import okio.Buffer;
+import okio.BufferedSource;
+import okio.GzipSource;
+import org.springblade.core.launch.log.BladeLogLevel;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Objects;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An OkHttp interceptor which logs request and response information. Can be applied as an
+ * {@linkplain OkHttpClient#interceptors() application interceptor} or as a {@linkplain
+ * OkHttpClient#networkInterceptors() network interceptor}. <p> The format of the logs created by
+ * this class should not be considered stable and may change slightly between releases. If you need
+ * a stable logging format, use your own interceptor.
+ *
+ * @author L.cm
+ */
+public final class HttpLoggingInterceptor implements Interceptor {
+	private static final Charset UTF8 = StandardCharsets.UTF_8;
+	private final Logger logger;
+	private volatile BladeLogLevel level = BladeLogLevel.NONE;
+
+	public interface Logger {
+		/**
+		 * log
+		 * @param message message
+		 */
+		void log(String message);
+	}
+
+	public HttpLoggingInterceptor(Logger logger) {
+		this.logger = logger;
+	}
+
+	/**
+	 * Change the level at which this interceptor logs.
+	 * @param level log Level
+	 * @return HttpLoggingInterceptor
+	 */
+	public HttpLoggingInterceptor setLevel(BladeLogLevel level) {
+		this.level = Objects.requireNonNull(level, "level == null. Use Level.NONE instead.");
+		return this;
+	}
+
+	public BladeLogLevel getLevel() {
+		return level;
+	}
+
+	@Override
+	public Response intercept(Chain chain) throws IOException {
+		BladeLogLevel level = this.level;
+
+		Request request = chain.request();
+		if (level == BladeLogLevel.NONE) {
+			return chain.proceed(request);
+		}
+
+		boolean logBody = level == BladeLogLevel.BODY;
+		boolean logHeaders = logBody || level == BladeLogLevel.HEADERS;
+
+		RequestBody requestBody = request.body();
+		boolean hasRequestBody = requestBody != null;
+
+		Connection connection = chain.connection();
+		String requestStartMessage = "--> "
+			+ request.method()
+			+ ' ' + request.url()
+			+ (connection != null ? " " + connection.protocol() : "");
+		if (!logHeaders && hasRequestBody) {
+			requestStartMessage += " (" + requestBody.contentLength() + "-byte body)";
+		}
+		logger.log(requestStartMessage);
+
+		if (logHeaders) {
+			if (hasRequestBody) {
+				// Request body headers are only present when installed as a network interceptor. Force
+				// them to be included (when available) so there values are known.
+				if (requestBody.contentType() != null) {
+					logger.log("Content-Type: " + requestBody.contentType());
+				}
+				if (requestBody.contentLength() != -1) {
+					logger.log("Content-Length: " + requestBody.contentLength());
+				}
+			}
+
+			Headers headers = request.headers();
+			for (int i = 0, count = headers.size(); i < count; i++) {
+				String name = headers.name(i);
+				// Skip headers from the request body as they are explicitly logged above.
+				if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) {
+					logger.log(name + ": " + headers.value(i));
+				}
+			}
+
+			if (!logBody || !hasRequestBody) {
+				logger.log("--> END " + request.method());
+			} else if (bodyHasUnknownEncoding(request.headers())) {
+				logger.log("--> END " + request.method() + " (encoded body omitted)");
+			} else {
+				Buffer buffer = new Buffer();
+				requestBody.writeTo(buffer);
+
+				Charset charset = UTF8;
+				MediaType contentType = requestBody.contentType();
+				if (contentType != null) {
+					charset = contentType.charset(UTF8);
+				}
+
+				logger.log("");
+				if (isPlaintext(buffer)) {
+					logger.log(buffer.readString(charset));
+					logger.log("--> END " + request.method()
+						+ " (" + requestBody.contentLength() + "-byte body)");
+				} else {
+					logger.log("--> END " + request.method() + " (binary "
+						+ requestBody.contentLength() + "-byte body omitted)");
+				}
+			}
+		}
+
+		long startNs = System.nanoTime();
+		Response response;
+		try {
+			response = chain.proceed(request);
+		} catch (Exception e) {
+			logger.log("<-- HTTP FAILED: " + e);
+			throw e;
+		}
+		long tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs);
+
+		ResponseBody responseBody = response.body();
+		long contentLength = responseBody.contentLength();
+		String bodySize = contentLength != -1 ? contentLength + "-byte" : "unknown-length";
+		logger.log("<-- "
+			+ response.code()
+			+ (response.message().isEmpty() ? "" : ' ' + response.message())
+			+ ' ' + response.request().url()
+			+ " (" + tookMs + "ms" + (!logHeaders ? ", " + bodySize + " body" : "") + ')');
+
+		if (logHeaders) {
+			Headers headers = response.headers();
+			int count = headers.size();
+			for (int i = 0; i < count; i++) {
+				logger.log(headers.name(i) + ": " + headers.value(i));
+			}
+
+			if (!logBody || !HttpHeaders.hasBody(response)) {
+				logger.log("<-- END HTTP");
+			} else if (bodyHasUnknownEncoding(response.headers())) {
+				logger.log("<-- END HTTP (encoded body omitted)");
+			} else {
+				BufferedSource source = responseBody.source();
+				// Buffer the entire body.
+				source.request(Long.MAX_VALUE);
+				Buffer buffer = source.getBuffer();
+
+				Long gzippedLength = null;
+				if ("gzip".equalsIgnoreCase(headers.get("Content-Encoding"))) {
+					gzippedLength = buffer.size();
+					GzipSource gzippedResponseBody = null;
+					try {
+						gzippedResponseBody = new GzipSource(buffer.clone());
+						buffer = new Buffer();
+						buffer.writeAll(gzippedResponseBody);
+					} finally {
+						if (gzippedResponseBody != null) {
+							gzippedResponseBody.close();
+						}
+					}
+				}
+
+				Charset charset = UTF8;
+				MediaType contentType = responseBody.contentType();
+				if (contentType != null) {
+					charset = contentType.charset(UTF8);
+				}
+
+				if (!isPlaintext(buffer)) {
+					logger.log("");
+					logger.log("<-- END HTTP (binary " + buffer.size() + "-byte body omitted)");
+					return response;
+				}
+
+				if (contentLength != 0) {
+					logger.log("");
+					logger.log(buffer.clone().readString(charset));
+				}
+
+				if (gzippedLength != null) {
+					logger.log("<-- END HTTP (" + buffer.size() + "-byte, "
+						+ gzippedLength + "-gzipped-byte body)");
+				} else {
+					logger.log("<-- END HTTP (" + buffer.size() + "-byte body)");
+				}
+			}
+		}
+
+		return response;
+	}
+
+	/**
+	 * Returns true if the body in question probably contains human readable text. Uses a small sample
+	 * of code points to detect unicode control characters commonly used in binary file signatures.
+	 */
+	private static boolean isPlaintext(Buffer buffer) {
+		try {
+			Buffer prefix = new Buffer();
+			long byteCount = buffer.size() < 64 ? buffer.size() : 64;
+			buffer.copyTo(prefix, 0, byteCount);
+			for (int i = 0; i < 16; i++) {
+				if (prefix.exhausted()) {
+					break;
+				}
+				int codePoint = prefix.readUtf8CodePoint();
+				if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
+					return false;
+				}
+			}
+			return true;
+		} catch (EOFException e) {
+			// Truncated UTF-8 sequence.
+			return false;
+		}
+	}
+
+	private boolean bodyHasUnknownEncoding(Headers headers) {
+		String contentEncoding = headers.get("Content-Encoding");
+		return contentEncoding != null
+			&& !"identity".equalsIgnoreCase(contentEncoding)
+			&& !"gzip".equalsIgnoreCase(contentEncoding);
+	}
+}

+ 33 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/http/logger/OkHttpSlf4jLogger.java

@@ -0,0 +1,33 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+
+package org.springblade.core.cloud.http.logger;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * OkHttp Slf4j logger
+ *
+ * @author L.cm
+ */
+@Slf4j
+public class OkHttpSlf4jLogger implements HttpLoggingInterceptor.Logger {
+	@Override
+	public void log(String message) {
+		log.info(message);
+	}
+}

+ 126 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/hystrix/BladeHystrixAutoConfiguration.java

@@ -0,0 +1,126 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.cloud.hystrix;
+
+import com.netflix.hystrix.Hystrix;
+import com.netflix.hystrix.strategy.HystrixPlugins;
+import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
+import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
+import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
+import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
+import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
+import feign.Contract;
+import feign.Feign;
+import feign.RequestInterceptor;
+import feign.hystrix.HystrixFeign;
+import org.springblade.core.cloud.feign.BladeFeignRequestInterceptor;
+import org.springblade.core.cloud.version.BladeSpringMvcContract;
+import org.springblade.core.tool.convert.EnumToStringConverter;
+import org.springblade.core.tool.convert.StringToEnumConverter;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.openfeign.BladeFeignClientsRegistrar;
+import org.springframework.cloud.openfeign.BladeHystrixTargeter;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.cloud.openfeign.Targeter;
+import org.springframework.context.annotation.*;
+import org.springframework.core.convert.ConversionService;
+import org.springframework.core.convert.converter.ConverterRegistry;
+import org.springframework.core.convert.support.DefaultConversionService;
+import org.springframework.lang.Nullable;
+
+import javax.annotation.PostConstruct;
+import java.util.ArrayList;
+
+/**
+ * Hystrix 配置
+ *
+ * @author L.cm
+ */
+@Configuration(proxyBeanMethods = false)
+@ConditionalOnClass({Hystrix.class, Feign.class})
+@Import(BladeFeignClientsRegistrar.class)
+@AutoConfigureAfter(EnableFeignClients.class)
+@ConditionalOnProperty(value = "feign.hystrix.enabled")
+public class BladeHystrixAutoConfiguration {
+	@Nullable
+	@Autowired(required = false)
+	private HystrixConcurrencyStrategy existingConcurrencyStrategy;
+
+	@PostConstruct
+	public void init() {
+		// Keeps references of existing Hystrix plugins.
+		HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
+		HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();
+		HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy();
+		HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance().getCommandExecutionHook();
+
+		HystrixPlugins.reset();
+
+		// Registers existing plugins excepts the Concurrent Strategy plugin.
+		HystrixConcurrencyStrategy strategy = new BladeHystrixConcurrencyStrategy(existingConcurrencyStrategy);
+		HystrixPlugins.getInstance().registerConcurrencyStrategy(strategy);
+		HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);
+		HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);
+		HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);
+		HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);
+	}
+
+	@Bean
+	@ConditionalOnMissingBean(Targeter.class)
+	public BladeHystrixTargeter bladeFeignTargeter() {
+		return new BladeHystrixTargeter();
+	}
+
+	@Bean
+	@Primary
+	@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
+	public Feign.Builder feignHystrixBuilder(RequestInterceptor requestInterceptor, Contract feignContract) {
+		return HystrixFeign.builder()
+			.contract(feignContract)
+			.decode404()
+			.requestInterceptor(requestInterceptor);
+	}
+
+	@Bean
+	@ConditionalOnMissingBean
+	public RequestInterceptor requestInterceptor() {
+		return new BladeFeignRequestInterceptor();
+	}
+
+	/**
+	 * blade enum 《-》 String 转换配置
+	 *
+	 * @param objectProvider ObjectProvider
+	 * @return SpringMvcContract
+	 */
+	@Bean
+	public Contract feignContract(@Qualifier("mvcConversionService") ObjectProvider<ConversionService> objectProvider) {
+		ConversionService conversionService = objectProvider.getIfAvailable(DefaultConversionService::new);
+		ConverterRegistry converterRegistry = ((ConverterRegistry) conversionService);
+		converterRegistry.addConverter(new EnumToStringConverter());
+		converterRegistry.addConverter(new StringToEnumConverter());
+		return new BladeSpringMvcContract(new ArrayList<>(), conversionService);
+	}
+
+}

+ 91 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/hystrix/BladeHystrixConcurrencyStrategy.java

@@ -0,0 +1,91 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.cloud.hystrix;
+
+import com.netflix.hystrix.HystrixThreadPoolKey;
+import com.netflix.hystrix.HystrixThreadPoolProperties;
+import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
+import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariable;
+import com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableLifecycle;
+import com.netflix.hystrix.strategy.properties.HystrixProperty;
+import lombok.AllArgsConstructor;
+import org.springblade.core.context.BladeCallableWrapper;
+import org.springframework.lang.Nullable;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Hystrix传递ThreaLocal中的一些变量
+ *
+ * <p>
+ * https://github.com/Netflix/Hystrix/issues/92#issuecomment-260548068
+ * https://github.com/spring-cloud/spring-cloud-sleuth/issues/39
+ * https://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/hystrix/security
+ * https://github.com/spring-projects/spring-security/blob/master/core/src/main/java/org/springframework/security/concurrent/DelegatingSecurityContextCallable.java
+ * </p>
+ *
+ * @author L.cm
+ */
+@AllArgsConstructor
+public class BladeHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {
+	@Nullable
+	private final HystrixConcurrencyStrategy existingConcurrencyStrategy;
+
+	@Override
+	public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
+		return existingConcurrencyStrategy != null
+			? existingConcurrencyStrategy.getBlockingQueue(maxQueueSize)
+			: super.getBlockingQueue(maxQueueSize);
+	}
+
+	@Override
+	public <T> HystrixRequestVariable<T> getRequestVariable(
+		HystrixRequestVariableLifecycle<T> rv) {
+		return existingConcurrencyStrategy != null
+			? existingConcurrencyStrategy.getRequestVariable(rv)
+			: super.getRequestVariable(rv);
+	}
+
+	@Override
+	public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
+											HystrixProperty<Integer> corePoolSize,
+											HystrixProperty<Integer> maximumPoolSize,
+											HystrixProperty<Integer> keepAliveTime, TimeUnit unit,
+											BlockingQueue<Runnable> workQueue) {
+		return existingConcurrencyStrategy != null
+			? existingConcurrencyStrategy.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue)
+			: super.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
+	}
+
+	@Override
+	public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties threadPoolProperties) {
+		return existingConcurrencyStrategy != null
+			? existingConcurrencyStrategy.getThreadPool(threadPoolKey, threadPoolProperties)
+			: super.getThreadPool(threadPoolKey, threadPoolProperties);
+	}
+
+	@Override
+	public <T> Callable<T> wrapCallable(Callable<T> callable) {
+		Callable<T> wrapCallable = new BladeCallableWrapper<>(callable);
+		return existingConcurrencyStrategy != null
+			? existingConcurrencyStrategy.wrapCallable(wrapCallable)
+			: super.wrapCallable(wrapCallable);
+	}
+}

+ 26 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeBlockExceptionHandler.java

@@ -0,0 +1,26 @@
+package org.springblade.core.cloud.sentinel;
+
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.jackson.JsonUtil;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Sentinel统一限流策略
+ *
+ * @author Chill
+ */
+public class BladeBlockExceptionHandler implements BlockExceptionHandler {
+	@Override
+	public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
+		// Return 429 (Too Many Requests) by default.
+		response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
+		response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+		response.getWriter().print(JsonUtil.toJson(R.fail(e.getMessage())));
+	}
+}

+ 128 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeFeignSentinel.java

@@ -0,0 +1,128 @@
+/*
+ * Copyright 2013-2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.cloud.sentinel;
+
+import com.alibaba.cloud.sentinel.feign.SentinelContractHolder;
+import feign.Contract;
+import feign.Feign;
+import feign.InvocationHandlerFactory;
+import feign.Target;
+import feign.hystrix.FallbackFactory;
+import lombok.SneakyThrows;
+import org.springblade.core.cloud.feign.BladeFallbackFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.cloud.openfeign.FeignContext;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.util.StringUtils;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+/**
+ * feign集成sentinel自动配置
+ * 重写 {@link com.alibaba.cloud.sentinel.feign.SentinelFeign} 适配最新API
+ *
+ * @author Chill
+ */
+public class BladeFeignSentinel {
+
+	public static Builder builder() {
+		return new Builder();
+	}
+
+	public static final class Builder extends Feign.Builder implements ApplicationContextAware {
+		private Contract contract = new Contract.Default();
+		private ApplicationContext applicationContext;
+		private FeignContext feignContext;
+
+		@Override
+		public Feign.Builder invocationHandlerFactory(
+			InvocationHandlerFactory invocationHandlerFactory) {
+			throw new UnsupportedOperationException();
+		}
+
+		@Override
+		public Builder contract(Contract contract) {
+			this.contract = contract;
+			return this;
+		}
+
+		@Override
+		public Feign build() {
+			super.invocationHandlerFactory(new InvocationHandlerFactory() {
+				@SneakyThrows
+				@Override
+				public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
+					// 注解取值以避免循环依赖的问题
+					FeignClient feignClient = AnnotationUtils.findAnnotation(target.type(), FeignClient.class);
+					Class fallback = feignClient.fallback();
+					Class fallbackFactory = feignClient.fallbackFactory();
+					String contextId = feignClient.contextId();
+
+					if (!StringUtils.hasText(contextId)) {
+						contextId = feignClient.name();
+					}
+
+					Object fallbackInstance;
+					FallbackFactory fallbackFactoryInstance;
+					// 判断fallback类型
+					if (void.class != fallback) {
+						fallbackInstance = getFromContext(contextId, "fallback", fallback, target.type());
+						return new BladeSentinelInvocationHandler(target, dispatch, new FallbackFactory.Default(fallbackInstance));
+					}
+					if (void.class != fallbackFactory) {
+						fallbackFactoryInstance = (FallbackFactory) getFromContext(contextId, "fallbackFactory", fallbackFactory, FallbackFactory.class);
+						return new BladeSentinelInvocationHandler(target, dispatch, fallbackFactoryInstance);
+					}
+					// 默认fallbackFactory
+					BladeFallbackFactory bladeFallbackFactory = new BladeFallbackFactory(target);
+					return new BladeSentinelInvocationHandler(target, dispatch, bladeFallbackFactory);
+				}
+
+				private Object getFromContext(String name, String type, Class fallbackType, Class targetType) {
+					Object fallbackInstance = feignContext.getInstance(name, fallbackType);
+					if (fallbackInstance == null) {
+						throw new IllegalStateException(
+							String.format("No %s instance of type %s found for feign client %s",
+								type, fallbackType, name)
+						);
+					}
+
+					if (!targetType.isAssignableFrom(fallbackType)) {
+						throw new IllegalStateException(
+							String.format("Incompatible %s instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
+								type, fallbackType, targetType, name)
+						);
+					}
+					return fallbackInstance;
+				}
+			});
+			super.contract(new SentinelContractHolder(contract));
+			return super.build();
+		}
+
+		@Override
+		public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+			this.applicationContext = applicationContext;
+			feignContext = this.applicationContext.getBean(FeignContext.class);
+		}
+	}
+
+}

+ 64 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeSentinelAutoConfiguration.java

@@ -0,0 +1,64 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.cloud.sentinel;
+
+import com.alibaba.cloud.sentinel.feign.SentinelFeignAutoConfiguration;
+import com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;
+import feign.Feign;
+import feign.RequestInterceptor;
+import lombok.AllArgsConstructor;
+import org.springblade.core.cloud.feign.BladeFeignRequestInterceptor;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.context.annotation.Scope;
+
+/**
+ * Sentinel配置类
+ *
+ * @author Chill
+ */
+@AllArgsConstructor
+@Configuration(proxyBeanMethods = false)
+@AutoConfigureBefore(SentinelFeignAutoConfiguration.class)
+@ConditionalOnProperty(name = "feign.sentinel.enabled")
+public class BladeSentinelAutoConfiguration {
+
+	@Bean
+	@Primary
+	@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
+	public Feign.Builder feignSentinelBuilder(RequestInterceptor requestInterceptor) {
+		return BladeFeignSentinel.builder().requestInterceptor(requestInterceptor);
+	}
+
+	@Bean
+	@ConditionalOnMissingBean
+	public RequestInterceptor requestInterceptor() {
+		return new BladeFeignRequestInterceptor();
+	}
+
+	@Bean
+	@ConditionalOnMissingBean
+	public BlockExceptionHandler blockExceptionHandler() {
+		return new BladeBlockExceptionHandler();
+	}
+
+}

+ 169 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/sentinel/BladeSentinelInvocationHandler.java

@@ -0,0 +1,169 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.cloud.sentinel;
+
+import com.alibaba.cloud.sentinel.feign.SentinelContractHolder;
+import com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.SphU;
+import com.alibaba.csp.sentinel.Tracer;
+import com.alibaba.csp.sentinel.context.ContextUtil;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import feign.Feign;
+import feign.InvocationHandlerFactory;
+import feign.MethodMetadata;
+import feign.Target;
+import feign.hystrix.FallbackFactory;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import static feign.Util.checkNotNull;
+
+/**
+ * 重写 {@link com.alibaba.cloud.sentinel.feign.SentinelInvocationHandler} 适配最新API
+ *
+ * @author Chill
+ */
+public class BladeSentinelInvocationHandler implements InvocationHandler {
+
+	private final Target<?> target;
+
+	private final Map<Method, InvocationHandlerFactory.MethodHandler> dispatch;
+
+	private FallbackFactory fallbackFactory;
+
+	private Map<Method, Method> fallbackMethodMap;
+
+	public BladeSentinelInvocationHandler(Target<?> target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch,
+								   FallbackFactory fallbackFactory) {
+		this.target = checkNotNull(target, "target");
+		this.dispatch = checkNotNull(dispatch, "dispatch");
+		this.fallbackFactory = fallbackFactory;
+		this.fallbackMethodMap = toFallbackMethod(dispatch);
+	}
+
+	public BladeSentinelInvocationHandler(Target<?> target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {
+		this.target = checkNotNull(target, "target");
+		this.dispatch = checkNotNull(dispatch, "dispatch");
+	}
+
+	@Override
+	public Object invoke(final Object proxy, final Method method, final Object[] args)
+		throws Throwable {
+		if ("equals".equals(method.getName())) {
+			try {
+				Object otherHandler = args.length > 0 && args[0] != null
+					? Proxy.getInvocationHandler(args[0]) : null;
+				return equals(otherHandler);
+			} catch (IllegalArgumentException e) {
+				return false;
+			}
+		} else if ("hashCode".equals(method.getName())) {
+			return hashCode();
+		} else if ("toString".equals(method.getName())) {
+			return toString();
+		}
+
+		Object result;
+		InvocationHandlerFactory.MethodHandler methodHandler = this.dispatch.get(method);
+		// only handle by HardCodedTarget
+		if (target instanceof Target.HardCodedTarget) {
+			Target.HardCodedTarget hardCodedTarget = (Target.HardCodedTarget) target;
+			MethodMetadata methodMetadata = SentinelContractHolder.METADATA_MAP
+				.get(hardCodedTarget.type().getName()
+					+ Feign.configKey(hardCodedTarget.type(), method));
+			// resource default is HttpMethod:protocol://url
+			if (methodMetadata == null) {
+				result = methodHandler.invoke(args);
+			} else {
+				String resourceName = methodMetadata.template().method().toUpperCase()
+					+ ":" + hardCodedTarget.url() + methodMetadata.template().path();
+				Entry entry = null;
+				try {
+					ContextUtil.enter(resourceName);
+					entry = SphU.entry(resourceName, EntryType.OUT, 1, args);
+					result = methodHandler.invoke(args);
+				} catch (Throwable ex) {
+					// fallback handle
+					if (!BlockException.isBlockException(ex)) {
+						Tracer.trace(ex);
+					}
+					if (fallbackFactory != null) {
+						try {
+							Object fallbackResult = fallbackMethodMap.get(method)
+								.invoke(fallbackFactory.create(ex), args);
+							return fallbackResult;
+						} catch (IllegalAccessException e) {
+							// shouldn't happen as method is public due to being an
+							// interface
+							throw new AssertionError(e);
+						} catch (InvocationTargetException e) {
+							throw new AssertionError(e.getCause());
+						}
+					} else {
+						// throw exception if fallbackFactory is null
+						throw ex;
+					}
+				} finally {
+					if (entry != null) {
+						entry.exit(1, args);
+					}
+					ContextUtil.exit();
+				}
+			}
+		} else {
+			// other target type using default strategy
+			result = methodHandler.invoke(args);
+		}
+
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (obj instanceof BladeSentinelInvocationHandler) {
+			BladeSentinelInvocationHandler other = (BladeSentinelInvocationHandler) obj;
+			return target.equals(other.target);
+		}
+		return false;
+	}
+
+	@Override
+	public int hashCode() {
+		return target.hashCode();
+	}
+
+	@Override
+	public String toString() {
+		return target.toString();
+	}
+
+	static Map<Method, Method> toFallbackMethod(Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {
+		Map<Method, Method> result = new LinkedHashMap<>();
+		for (Method method : dispatch.keySet()) {
+			method.setAccessible(true);
+			result.put(method, method);
+		}
+		return result;
+	}
+
+}

+ 46 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/server/UndertowHttp2Configuration.java

@@ -0,0 +1,46 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.cloud.server;
+
+import io.undertow.Undertow;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration;
+import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
+import org.springframework.boot.web.server.WebServerFactoryCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import static io.undertow.UndertowOptions.ENABLE_HTTP2;
+
+
+/**
+ * Undertow http2 h2c 配置,对 servlet 开启
+ *
+ * @author L.cm
+ */
+@Configuration(proxyBeanMethods = false)
+@ConditionalOnClass(Undertow.class)
+@AutoConfigureBefore(ServletWebServerFactoryAutoConfiguration.class)
+public class UndertowHttp2Configuration {
+
+	@Bean
+	public WebServerFactoryCustomizer<UndertowServletWebServerFactory> undertowHttp2WebServerFactoryCustomizer() {
+		return factory -> factory.addBuilderCustomizers(builder -> builder.setServerOption(ENABLE_HTTP2, true));
+	}
+
+}

+ 49 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/version/BladeMediaType.java

@@ -0,0 +1,49 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+
+package org.springblade.core.cloud.version;
+
+import lombok.Getter;
+import org.springframework.http.MediaType;
+
+/**
+ * blade Media Types,application/vnd.github.VERSION+json
+ *
+ * <p>
+ * https://developer.github.com/v3/media/
+ * </p>
+ *
+ * @author L.cm
+ */
+@Getter
+public class BladeMediaType {
+	private static final String MEDIA_TYPE_TEMP = "application/vnd.%s.%s+json";
+
+	private final String appName = "blade";
+	private final String version;
+	private final MediaType mediaType;
+
+	public BladeMediaType(String version) {
+		this.version = version;
+		this.mediaType = MediaType.valueOf(String.format(MEDIA_TYPE_TEMP, appName, version));
+	}
+
+	@Override
+	public String toString() {
+		return mediaType.toString();
+	}
+}

+ 104 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/version/BladeRequestMappingHandlerMapping.java

@@ -0,0 +1,104 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.cloud.version;
+
+import org.springblade.core.cloud.annotation.ApiVersion;
+import org.springblade.core.cloud.annotation.UrlVersion;
+import org.springblade.core.tool.utils.StringPool;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+import org.springframework.lang.Nullable;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+
+/**
+ * url版本号处理 和 header 版本处理
+ *
+ * <p>
+ *     url: /v1/user/{id}
+ *     header: Accept application/vnd.blade.VERSION+json
+ * </p>
+ *
+ * 注意:c 代表客户端版本
+ *
+ * @author L.cm
+ */
+public class BladeRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
+
+	@Nullable
+	@Override
+	protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
+		RequestMappingInfo mappinginfo = super.getMappingForMethod(method, handlerType);
+		if (mappinginfo != null) {
+			RequestMappingInfo apiVersionMappingInfo = getApiVersionMappingInfo(method, handlerType);
+			return apiVersionMappingInfo == null ? mappinginfo : apiVersionMappingInfo.combine(mappinginfo);
+		}
+		return null;
+	}
+
+	@Nullable
+	private RequestMappingInfo getApiVersionMappingInfo(Method method, Class<?> handlerType) {
+		// url 上的版本,优先获取方法上的版本
+		UrlVersion urlVersion = AnnotatedElementUtils.findMergedAnnotation(method, UrlVersion.class);
+		// 再次尝试类上的版本
+		if (urlVersion == null || StringUtil.isBlank(urlVersion.value())) {
+			urlVersion = AnnotatedElementUtils.findMergedAnnotation(handlerType, UrlVersion.class);
+		}
+		// Media Types 版本信息
+		ApiVersion apiVersion = AnnotatedElementUtils.findMergedAnnotation(method, ApiVersion.class);
+		// 再次尝试类上的版本
+		if (apiVersion == null || StringUtil.isBlank(apiVersion.value())) {
+			apiVersion = AnnotatedElementUtils.findMergedAnnotation(handlerType, ApiVersion.class);
+		}
+		boolean nonUrlVersion = urlVersion == null || StringUtil.isBlank(urlVersion.value());
+		boolean nonApiVersion = apiVersion == null || StringUtil.isBlank(apiVersion.value());
+		// 先判断同时不纯在
+		if (nonUrlVersion && nonApiVersion) {
+			return null;
+		}
+		// 如果 header 版本不存在
+		RequestMappingInfo.Builder mappingInfoBuilder = null;
+		if (nonApiVersion) {
+			mappingInfoBuilder = RequestMappingInfo.paths(urlVersion.value());
+		} else {
+			mappingInfoBuilder = RequestMappingInfo.paths(StringPool.EMPTY);
+		}
+		// 如果url版本不存在
+		if (nonUrlVersion) {
+			String vsersionMediaTypes = new BladeMediaType(apiVersion.value()).toString();
+			mappingInfoBuilder.produces(vsersionMediaTypes);
+		}
+		return mappingInfoBuilder.build();
+	}
+
+	@Override
+	protected void handlerMethodsInitialized(Map<RequestMappingInfo, HandlerMethod> handlerMethods) {
+		// 打印路由信息 spring boot 2.1 去掉了这个 日志的打印
+		if (logger.isInfoEnabled()) {
+			for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethods.entrySet()) {
+				RequestMappingInfo mapping = entry.getKey();
+				HandlerMethod handlerMethod = entry.getValue();
+				logger.info("Mapped \"" + mapping + "\" onto " + handlerMethod);
+			}
+		}
+		super.handlerMethodsInitialized(handlerMethods);
+	}
+}

+ 101 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/version/BladeSpringMvcContract.java

@@ -0,0 +1,101 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.cloud.version;
+
+import feign.MethodMetadata;
+import org.springblade.core.cloud.annotation.ApiVersion;
+import org.springblade.core.cloud.annotation.UrlVersion;
+import org.springblade.core.tool.utils.StringPool;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
+import org.springframework.cloud.openfeign.support.SpringMvcContract;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+import org.springframework.core.convert.ConversionService;
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * 支持 blade-boot 的 版本 处理
+ *
+ * @see org.springblade.core.cloud.annotation.UrlVersion
+ * @see org.springblade.core.cloud.annotation.ApiVersion
+ * @author L.cm
+ */
+public class BladeSpringMvcContract extends SpringMvcContract {
+
+	public BladeSpringMvcContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors, ConversionService conversionService) {
+		super(annotatedParameterProcessors, conversionService);
+	}
+
+	@Override
+	protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) {
+		if (RequestMapping.class.isInstance(methodAnnotation) || methodAnnotation.annotationType().isAnnotationPresent(RequestMapping.class)) {
+			Class<?> targetType = method.getDeclaringClass();
+			// url 上的版本,优先获取方法上的版本
+			UrlVersion urlVersion = AnnotatedElementUtils.findMergedAnnotation(method, UrlVersion.class);
+			// 再次尝试类上的版本
+			if (urlVersion == null || StringUtil.isBlank(urlVersion.value())) {
+				urlVersion = AnnotatedElementUtils.findMergedAnnotation(targetType, UrlVersion.class);
+			}
+			if (urlVersion != null && StringUtil.isNotBlank(urlVersion.value())) {
+				String versionUrl = "/" + urlVersion.value();
+				data.template().uri(versionUrl);
+			}
+
+			// 注意:在父类之前 添加 url版本,在父类之后,处理 Media Types 版本
+			super.processAnnotationOnMethod(data, methodAnnotation, method);
+
+			// 处理 Media Types 版本信息
+			ApiVersion apiVersion = AnnotatedElementUtils.findMergedAnnotation(method, ApiVersion.class);
+			// 再次尝试类上的版本
+			if (apiVersion == null || StringUtil.isBlank(apiVersion.value())) {
+				apiVersion = AnnotatedElementUtils.findMergedAnnotation(targetType, ApiVersion.class);
+			}
+			if (apiVersion != null && StringUtil.isNotBlank(apiVersion.value())) {
+				BladeMediaType bladeMediaType = new BladeMediaType(apiVersion.value());
+				data.template().header(HttpHeaders.ACCEPT, bladeMediaType.toString());
+			}
+		}
+	}
+
+	/**
+	 * 参考:https://gist.github.com/rmfish/0ed59a9af6c05157be2a60c9acea2a10
+	 * @param annotations 注解
+	 * @param paramIndex 参数索引
+	 * @return 是否 http 注解
+	 */
+	@Override
+	protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
+		boolean httpAnnotation = super.processAnnotationsOnParameter(data, annotations, paramIndex);
+		// 在 springMvc 中如果是 Get 请求且参数中是对象 没有声明为@RequestBody 则默认为 Param
+		if (!httpAnnotation && StringPool.GET.equals(data.template().method().toUpperCase())) {
+			for (Annotation parameterAnnotation : annotations) {
+				if (!(parameterAnnotation instanceof RequestBody)) {
+					return false;
+				}
+			}
+			data.queryMapIndex(paramIndex);
+			return true;
+		}
+		return httpAnnotation;
+	}
+}

+ 44 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/version/BladeWebMvcRegistrations.java

@@ -0,0 +1,44 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.cloud.version;
+
+import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
+import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+
+/**
+ * url版本号处理
+ *
+ * @author L.cm
+ */
+public class BladeWebMvcRegistrations implements WebMvcRegistrations {
+	@Override
+	public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
+		return new BladeRequestMappingHandlerMapping();
+	}
+
+	@Override
+	public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
+		return null;
+	}
+
+	@Override
+	public ExceptionHandlerExceptionResolver getExceptionHandlerExceptionResolver() {
+		return null;
+	}
+}

+ 38 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springblade/core/cloud/version/VersionMappingAutoConfiguration.java

@@ -0,0 +1,38 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.cloud.version;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * url版本号处理
+ *
+ * 参考:https://gitee.com/lianqu1990/spring-boot-starter-version-mapping
+ *
+ * @author L.cm
+ */
+@Configuration(proxyBeanMethods = false)
+@ConditionalOnWebApplication
+public class VersionMappingAutoConfiguration {
+	@Bean
+	public WebMvcRegistrations bladeWebMvcRegistrations() {
+		return new BladeWebMvcRegistrations();
+	}
+}

+ 229 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springframework/cloud/openfeign/BladeFeignClientsRegistrar.java

@@ -0,0 +1,229 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springframework.cloud.openfeign;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import org.springblade.core.cloud.hystrix.BladeHystrixAutoConfiguration;
+import org.springframework.beans.factory.BeanClassLoaderAware;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.context.EnvironmentAware;
+import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
+import org.springframework.core.annotation.AnnotatedElementUtils;
+import org.springframework.core.annotation.AnnotationAttributes;
+import org.springframework.core.env.Environment;
+import org.springframework.core.io.support.SpringFactoriesLoader;
+import org.springframework.core.type.AnnotationMetadata;
+import org.springframework.lang.Nullable;
+import org.springframework.util.StringUtils;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * feign 自动配置
+ *
+ * @author L.cm
+ */
+@NoArgsConstructor
+public class BladeFeignClientsRegistrar implements ImportBeanDefinitionRegistrar, BeanClassLoaderAware, EnvironmentAware {
+	@Getter
+	private ClassLoader beanClassLoader;
+	@Getter
+	private Environment environment;
+
+	@Override
+	public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
+		registerFeignClients(metadata, registry);
+	}
+
+	@Override
+	public void setBeanClassLoader(ClassLoader classLoader) {
+		this.beanClassLoader = classLoader;
+	}
+
+	private void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
+		List<String> feignClients = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
+		// 如果 spring.factories 里为空
+		if (feignClients.isEmpty()) {
+			return;
+		}
+		for (String className : feignClients) {
+			try {
+				Class<?> clazz = beanClassLoader.loadClass(className);
+				AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(clazz, FeignClient.class);
+				if (attributes == null) {
+					continue;
+				}
+				// 如果已经存在该 bean,支持原生的 Feign
+				if (registry.containsBeanDefinition(className)) {
+					continue;
+				}
+				registerClientConfiguration(registry, getClientName(attributes), attributes.get("configuration"));
+
+				validate(attributes);
+				BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
+				definition.addPropertyValue("url", getUrl(attributes));
+				definition.addPropertyValue("path", getPath(attributes));
+				String name = getName(attributes);
+				definition.addPropertyValue("name", name);
+
+				// 兼容最新版本的 spring-cloud-openfeign,尚未发布
+				StringBuilder aliasBuilder = new StringBuilder(18);
+				if (attributes.containsKey("contextId")) {
+					String contextId = getContextId(attributes);
+					aliasBuilder.append(contextId);
+					definition.addPropertyValue("contextId", contextId);
+				} else {
+					aliasBuilder.append(name);
+				}
+
+				definition.addPropertyValue("type", className);
+				definition.addPropertyValue("decode404", attributes.get("decode404"));
+				definition.addPropertyValue("fallback", attributes.get("fallback"));
+				definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
+				definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
+
+				AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
+
+				// alias
+				String alias = aliasBuilder.append("FeignClient").toString();
+
+				// has a default, won't be null
+				boolean primary = (Boolean)attributes.get("primary");
+
+				beanDefinition.setPrimary(primary);
+
+				String qualifier = getQualifier(attributes);
+				if (StringUtils.hasText(qualifier)) {
+					alias = qualifier;
+				}
+
+				BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[] { alias });
+				BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
+
+			} catch (ClassNotFoundException e) {
+				e.printStackTrace();
+			}
+		}
+	}
+
+	/**
+	 * Return the class used by {@link SpringFactoriesLoader} to load configuration
+	 * candidates.
+	 * @return the factory class
+	 */
+	private Class<?> getSpringFactoriesLoaderFactoryClass() {
+		return BladeHystrixAutoConfiguration.class;
+	}
+
+	private void validate(Map<String, Object> attributes) {
+		AnnotationAttributes annotation = AnnotationAttributes.fromMap(attributes);
+		FeignClientsRegistrar.validateFallback(annotation.getClass("fallback"));
+		FeignClientsRegistrar.validateFallbackFactory(annotation.getClass("fallbackFactory"));
+	}
+
+	private String getName(Map<String, Object> attributes) {
+		String name = (String) attributes.get("serviceId");
+		if (!StringUtils.hasText(name)) {
+			name = (String) attributes.get("name");
+		}
+		if (!StringUtils.hasText(name)) {
+			name = (String) attributes.get("value");
+		}
+		name = resolve(name);
+		return FeignClientsRegistrar.getName(name);
+	}
+
+	private String getContextId(Map<String, Object> attributes) {
+		String contextId = (String) attributes.get("contextId");
+		if (!StringUtils.hasText(contextId)) {
+			return getName(attributes);
+		}
+
+		contextId = resolve(contextId);
+		return FeignClientsRegistrar.getName(contextId);
+	}
+
+	private String resolve(String value) {
+		if (StringUtils.hasText(value)) {
+			return this.environment.resolvePlaceholders(value);
+		}
+		return value;
+	}
+
+	private String getUrl(Map<String, Object> attributes) {
+		String url = resolve((String) attributes.get("url"));
+		return FeignClientsRegistrar.getUrl(url);
+	}
+
+	private String getPath(Map<String, Object> attributes) {
+		String path = resolve((String) attributes.get("path"));
+		return FeignClientsRegistrar.getPath(path);
+	}
+
+	@Nullable
+	private String getQualifier(@Nullable Map<String, Object> client) {
+		if (client == null) {
+			return null;
+		}
+		String qualifier = (String) client.get("qualifier");
+		if (StringUtils.hasText(qualifier)) {
+			return qualifier;
+		}
+		return null;
+	}
+
+	@Nullable
+	private String getClientName(@Nullable Map<String, Object> client) {
+		if (client == null) {
+			return null;
+		}
+		String value = (String) client.get("contextId");
+		if (!StringUtils.hasText(value)) {
+			value = (String) client.get("value");
+		}
+		if (!StringUtils.hasText(value)) {
+			value = (String) client.get("name");
+		}
+		if (!StringUtils.hasText(value)) {
+			value = (String) client.get("serviceId");
+		}
+		if (StringUtils.hasText(value)) {
+			return value;
+		}
+
+		throw new IllegalStateException("Either 'name' or 'value' must be provided in @" + FeignClient.class.getSimpleName());
+	}
+
+	private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
+		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
+		builder.addConstructorArgValue(name);
+		builder.addConstructorArgValue(configuration);
+		registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(), builder.getBeanDefinition());
+	}
+
+	@Override
+	public void setEnvironment(Environment environment) {
+		this.environment = environment;
+	}
+
+}

+ 99 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springframework/cloud/openfeign/BladeHystrixTargeter.java

@@ -0,0 +1,99 @@
+/*
+ * Copyright 2013-2018 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.springframework.cloud.openfeign;
+
+import feign.Feign;
+import feign.Target;
+import feign.hystrix.FallbackFactory;
+import feign.hystrix.HystrixFeign;
+import feign.hystrix.SetterFactory;
+import org.springblade.core.cloud.feign.BladeFallbackFactory;
+import org.springframework.lang.Nullable;
+
+/**
+ * 添加 blade 默认的 fallbackFactory L.cm 2019.01.19
+ *
+ * @author L.cm
+ * @author Spencer Gibb
+ * @author Erik Kringen
+ */
+@SuppressWarnings("unchecked")
+public class BladeHystrixTargeter implements Targeter {
+
+	@Override
+	public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
+						Target.HardCodedTarget<T> target) {
+		if (!(feign instanceof HystrixFeign.Builder)) {
+			return feign.target(target);
+		}
+		HystrixFeign.Builder builder = (HystrixFeign.Builder) feign;
+		SetterFactory setterFactory = getOptional(factory.getName(), context, SetterFactory.class);
+		if (setterFactory != null) {
+			builder.setterFactory(setterFactory);
+		}
+		Class<?> fallback = factory.getFallback();
+		if (fallback != void.class) {
+			return targetWithFallback(factory.getName(), context, target, builder, fallback);
+		}
+		Class<?> fallbackFactory = factory.getFallbackFactory();
+		if (fallbackFactory != void.class) {
+			return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
+		}
+		// blade 默认的 fallbackFactory
+		BladeFallbackFactory bladeFallbackFactory = new BladeFallbackFactory(target);
+		return (T) builder.target(target, bladeFallbackFactory);
+	}
+
+	private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
+											Target.HardCodedTarget<T> target,
+											HystrixFeign.Builder builder,
+											Class<?> fallbackFactoryClass) {
+		FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>)
+			getFromContext("fallbackFactory", feignClientName, context, fallbackFactoryClass, FallbackFactory.class);
+		return builder.target(target, fallbackFactory);
+	}
+
+
+	private <T> T targetWithFallback(String feignClientName, FeignContext context,
+									 Target.HardCodedTarget<T> target,
+									 HystrixFeign.Builder builder, Class<?> fallback) {
+		T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type());
+		return builder.target(target, fallbackInstance);
+	}
+
+	private <T> T getFromContext(String fallbackMechanism, String feignClientName, FeignContext context, Class<?> beanType,
+								 Class<T> targetType) {
+		Object fallbackInstance = context.getInstance(feignClientName, beanType);
+		if (fallbackInstance == null) {
+			throw new IllegalStateException(String.format("No " + fallbackMechanism +
+				" instance of type %s found for feign client %s", beanType, feignClientName));
+		}
+
+		if (!targetType.isAssignableFrom(beanType)) {
+			throw new IllegalStateException(String.format(
+				"Incompatible " + fallbackMechanism + " instance. Fallback/fallbackFactory of " +
+					"type %s is not assignable to %s for feign client %s", beanType, targetType, feignClientName));
+		}
+		return (T) fallbackInstance;
+	}
+
+	@Nullable
+	private <T> T getOptional(String feignClientName, FeignContext context, Class<T> beanType) {
+		return context.getInstance(feignClientName, beanType);
+	}
+}

+ 39 - 0
bladex-tool/blade-core-cloud/src/main/java/org/springframework/cloud/openfeign/Targeter.java

@@ -0,0 +1,39 @@
+/*
+ * Copyright 2013-2016 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.springframework.cloud.openfeign;
+
+import feign.Feign;
+import feign.Target;
+
+/**
+ * @author Spencer Gibb
+ */
+public interface Targeter {
+	/**
+	 * target
+	 *
+	 * @param factory
+	 * @param feign
+	 * @param context
+	 * @param target
+	 * @param <T>
+	 * @return T
+	 */
+	<T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
+				 Target.HardCodedTarget<T> target);
+}

+ 31 - 0
bladex-tool/blade-core-context/pom.xml

@@ -0,0 +1,31 @@
+<?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>BladeX-Tool</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.8.2.RELEASE</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-core-context</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${project.parent.version}</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-tool</artifactId>
+        </dependency>
+        <!-- Auto -->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-auto</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+
+</project>

+ 65 - 0
bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/BladeCallableWrapper.java

@@ -0,0 +1,65 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.context;
+
+import org.slf4j.MDC;
+import org.springblade.core.tool.utils.ThreadLocalUtil;
+import org.springframework.lang.Nullable;
+
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+/**
+ * 多线程中传递 context 和 mdc
+ *
+ * @author L.cm
+ */
+public class BladeCallableWrapper<V> implements Callable<V> {
+	private final Callable<V> delegate;
+	private final Map<String, Object> tlMap;
+	/**
+	 * logback 下有可能为 null
+	 */
+	@Nullable
+	private final Map<String, String> mdcMap;
+
+	public BladeCallableWrapper(Callable<V> callable) {
+		this.delegate = callable;
+		this.tlMap = ThreadLocalUtil.getAll();
+		this.mdcMap = MDC.getCopyOfContextMap();
+	}
+
+	@Override
+	public V call() throws Exception {
+		if (!tlMap.isEmpty()) {
+			ThreadLocalUtil.put(tlMap);
+		}
+		if (mdcMap != null && !mdcMap.isEmpty()) {
+			MDC.setContextMap(mdcMap);
+		}
+		try {
+			return delegate.call();
+		} finally {
+			tlMap.clear();
+			if (mdcMap != null) {
+				mdcMap.clear();
+			}
+			ThreadLocalUtil.clear();
+			MDC.clear();
+		}
+	}
+}

+ 73 - 0
bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/BladeContext.java

@@ -0,0 +1,73 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.context;
+
+import org.springframework.lang.Nullable;
+
+import java.util.function.Function;
+
+/**
+ * Blade微服务上下文
+ *
+ * @author L.cm
+ */
+public interface BladeContext {
+
+	/**
+	 * 获取 请求 id
+	 *
+	 * @return 请求id
+	 */
+	@Nullable
+	String getRequestId();
+
+	/**
+	 * 账号id
+	 *
+	 * @return 账号id
+	 */
+	@Nullable
+	String getAccountId();
+
+	/**
+	 * 获取租户id
+	 *
+	 * @return 租户id
+	 */
+	@Nullable
+	String getTenantId();
+
+	/**
+	 * 获取上下文中的数据
+	 *
+	 * @param ctxKey 上下文中的key
+	 * @return 返回对象
+	 */
+	@Nullable
+	String get(String ctxKey);
+
+	/**
+	 * 获取上下文中的数据
+	 *
+	 * @param ctxKey   上下文中的key
+	 * @param function 函数式
+	 * @param <T>      泛型对象
+	 * @return 返回对象
+	 */
+	@Nullable
+	<T> T get(String ctxKey, Function<String, T> function);
+}

+ 50 - 0
bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/BladeHttpHeadersGetter.java

@@ -0,0 +1,50 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.context;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.lang.Nullable;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * HttpHeaders 获取器,用于跨服务和线程的传递,
+ * <p>
+ * 暂时不支持 webflux。
+ *
+ * @author L.cm
+ */
+public interface BladeHttpHeadersGetter {
+
+	/**
+	 * 获取 HttpHeaders
+	 *
+	 * @return HttpHeaders
+	 */
+	@Nullable
+	HttpHeaders get();
+
+	/**
+	 * 获取 HttpHeaders
+	 *
+	 * @param request 请求
+	 * @return HttpHeaders
+	 */
+	@Nullable
+	HttpHeaders get(HttpServletRequest request);
+
+}

+ 64 - 0
bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/BladeRunnableWrapper.java

@@ -0,0 +1,64 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.context;
+
+import org.slf4j.MDC;
+import org.springblade.core.tool.utils.ThreadLocalUtil;
+import org.springframework.lang.Nullable;
+
+import java.util.Map;
+
+/**
+ * 多线程中传递 context 和 mdc
+ *
+ * @author L.cm
+ */
+public class BladeRunnableWrapper implements Runnable {
+	private final Runnable delegate;
+	private final Map<String, Object> tlMap;
+	/**
+	 * logback 下有可能为 null
+	 */
+	@Nullable
+	private final Map<String, String> mdcMap;
+
+	public BladeRunnableWrapper(Runnable runnable) {
+		this.delegate = runnable;
+		this.tlMap = ThreadLocalUtil.getAll();
+		this.mdcMap = MDC.getCopyOfContextMap();
+	}
+
+	@Override
+	public void run() {
+		if (!tlMap.isEmpty()) {
+			ThreadLocalUtil.put(tlMap);
+		}
+		if (mdcMap != null && !mdcMap.isEmpty()) {
+			MDC.setContextMap(mdcMap);
+		}
+		try {
+			delegate.run();
+		} finally {
+			tlMap.clear();
+			if (mdcMap != null) {
+				mdcMap.clear();
+			}
+			ThreadLocalUtil.clear();
+			MDC.clear();
+		}
+	}
+}

+ 80 - 0
bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/BladeServletContext.java

@@ -0,0 +1,80 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.context;
+
+import lombok.RequiredArgsConstructor;
+import org.springblade.core.context.props.BladeContextProperties;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.core.tool.utils.ThreadLocalUtil;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.http.HttpHeaders;
+import org.springframework.lang.Nullable;
+
+import java.util.function.Function;
+
+import static org.springblade.core.tool.constant.BladeConstant.CONTEXT_KEY;
+
+/**
+ * blade servlet 上下文,跨线程失效
+ *
+ * @author L.cm
+ */
+@RequiredArgsConstructor
+@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
+public class BladeServletContext implements BladeContext {
+	private final BladeContextProperties contextProperties;
+	private final BladeHttpHeadersGetter httpHeadersGetter;
+
+	@Nullable
+	@Override
+	public String getRequestId() {
+		return get(contextProperties.getHeaders().getRequestId());
+	}
+
+	@Nullable
+	@Override
+	public String getAccountId() {
+		return get(contextProperties.getHeaders().getAccountId());
+	}
+
+	@Nullable
+	@Override
+	public String getTenantId() {
+		return get(contextProperties.getHeaders().getTenantId());
+	}
+
+	@Nullable
+	@Override
+	public String get(String ctxKey) {
+		HttpHeaders headers = ThreadLocalUtil.getIfAbsent(CONTEXT_KEY, httpHeadersGetter::get);
+		if (headers == null || headers.isEmpty()) {
+			return null;
+		}
+		return headers.getFirst(ctxKey);
+	}
+
+	@Nullable
+	@Override
+	public <T> T get(String ctxKey, Function<String, T> function) {
+		String ctxValue = get(ctxKey);
+		if (StringUtil.isBlank(ctxValue)) {
+			return null;
+		}
+		return function.apply(ctxKey);
+	}
+
+}

+ 75 - 0
bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/ServletHttpHeadersGetter.java

@@ -0,0 +1,75 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.context;
+
+import lombok.RequiredArgsConstructor;
+import org.springblade.core.context.props.BladeContextProperties;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.core.tool.utils.WebUtil;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.http.HttpHeaders;
+import org.springframework.lang.Nullable;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * HttpHeaders 获取器
+ *
+ * @author L.cm
+ */
+@RequiredArgsConstructor
+@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
+public class ServletHttpHeadersGetter implements BladeHttpHeadersGetter {
+	private final BladeContextProperties properties;
+
+	@Nullable
+	@Override
+	public HttpHeaders get() {
+		HttpServletRequest request = WebUtil.getRequest();
+		if (request == null) {
+			return null;
+		}
+		return get(request);
+	}
+
+	@Nullable
+	@Override
+	public HttpHeaders get(HttpServletRequest request) {
+		HttpHeaders headers = new HttpHeaders();
+		List<String> crossHeaders = properties.getCrossHeaders();
+		// 传递请求头
+		Enumeration<String> headerNames = request.getHeaderNames();
+		if (headerNames != null) {
+			List<String> allowed = properties.getHeaders().getAllowed();
+			while (headerNames.hasMoreElements()) {
+				String key = headerNames.nextElement();
+				// 只支持配置的 header
+				if (crossHeaders.contains(key) || allowed.contains(key)) {
+					String values = request.getHeader(key);
+					// header value 不为空的 传递
+					if (StringUtil.isNotBlank(values)) {
+						headers.add(key, values);
+					}
+				}
+			}
+		}
+		return headers;
+	}
+
+}

+ 53 - 0
bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/config/BladeContextAutoConfiguration.java

@@ -0,0 +1,53 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.context.config;
+
+import org.springblade.core.context.BladeContext;
+import org.springblade.core.context.BladeHttpHeadersGetter;
+import org.springblade.core.context.BladeServletContext;
+import org.springblade.core.context.ServletHttpHeadersGetter;
+import org.springblade.core.context.props.BladeContextProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+
+/**
+ * blade 服务上下文配置
+ *
+ * @author L.cm
+ */
+@Configuration(proxyBeanMethods = false)
+@Order(Ordered.HIGHEST_PRECEDENCE)
+@EnableConfigurationProperties(BladeContextProperties.class)
+public class BladeContextAutoConfiguration {
+
+	@Bean
+	@ConditionalOnMissingBean
+	public BladeHttpHeadersGetter bladeHttpHeadersGetter(BladeContextProperties contextProperties) {
+		return new ServletHttpHeadersGetter(contextProperties);
+	}
+
+	@Bean
+	@ConditionalOnMissingBean
+	public BladeContext bladeContext(BladeContextProperties contextProperties, BladeHttpHeadersGetter httpHeadersGetter) {
+		return new BladeServletContext(contextProperties, httpHeadersGetter);
+	}
+
+}

+ 42 - 0
bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/config/BladeServletListenerConfiguration.java

@@ -0,0 +1,42 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.context.config;
+
+import org.springblade.core.context.BladeHttpHeadersGetter;
+import org.springblade.core.context.listener.BladeServletRequestListener;
+import org.springblade.core.context.props.BladeContextProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Servlet 监听器自动配置
+ *
+ * @author L.cm
+ */
+@Configuration(proxyBeanMethods = false)
+@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
+public class BladeServletListenerConfiguration {
+
+	@Bean
+	public ServletListenerRegistrationBean<?> registerCustomListener(BladeContextProperties properties,
+																	 BladeHttpHeadersGetter httpHeadersGetter) {
+		return new ServletListenerRegistrationBean<>(new BladeServletRequestListener(properties, httpHeadersGetter));
+	}
+
+}

+ 74 - 0
bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/listener/BladeServletRequestListener.java

@@ -0,0 +1,74 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.context.listener;
+
+import lombok.RequiredArgsConstructor;
+import org.slf4j.MDC;
+import org.springblade.core.context.BladeHttpHeadersGetter;
+import org.springblade.core.context.props.BladeContextProperties;
+import org.springblade.core.tool.constant.BladeConstant;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.core.tool.utils.ThreadLocalUtil;
+import org.springframework.http.HttpHeaders;
+
+import javax.servlet.ServletRequestEvent;
+import javax.servlet.ServletRequestListener;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Servlet 请求监听器
+ *
+ * @author L.cm
+ */
+@RequiredArgsConstructor
+public class BladeServletRequestListener implements ServletRequestListener {
+	private final BladeContextProperties contextProperties;
+	private final BladeHttpHeadersGetter httpHeadersGetter;
+
+	@Override
+	public void requestInitialized(ServletRequestEvent event) {
+		HttpServletRequest request = (HttpServletRequest) event.getServletRequest();
+		// MDC 获取透传的 变量
+		BladeContextProperties.Headers headers = contextProperties.getHeaders();
+		String requestId = request.getHeader(headers.getRequestId());
+		if (StringUtil.isNotBlank(requestId)) {
+			MDC.put(BladeConstant.MDC_REQUEST_ID_KEY, requestId);
+		}
+		String accountId = request.getHeader(headers.getAccountId());
+		if (StringUtil.isNotBlank(accountId)) {
+			MDC.put(BladeConstant.MDC_ACCOUNT_ID_KEY, accountId);
+		}
+		String tenantId = request.getHeader(headers.getTenantId());
+		if (StringUtil.isNotBlank(tenantId)) {
+			MDC.put(BladeConstant.MDC_TENANT_ID_KEY, tenantId);
+		}
+		// 处理 context,直接传递 request,因为 spring 中的尚未初始化完成
+		HttpHeaders httpHeaders = httpHeadersGetter.get(request);
+		ThreadLocalUtil.put(BladeConstant.CONTEXT_KEY, httpHeaders);
+	}
+
+	@Override
+	public void requestDestroyed(ServletRequestEvent event) {
+		// 会话销毁时,清除上下文
+		ThreadLocalUtil.clear();
+		// 会话销毁时,清除 mdc
+		MDC.remove(BladeConstant.MDC_REQUEST_ID_KEY);
+		MDC.remove(BladeConstant.MDC_ACCOUNT_ID_KEY);
+		MDC.remove(BladeConstant.MDC_TENANT_ID_KEY);
+	}
+
+}

+ 81 - 0
bladex-tool/blade-core-context/src/main/java/org/springblade/core/context/props/BladeContextProperties.java

@@ -0,0 +1,81 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.core.context.props;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springblade.core.launch.constant.TokenConstant;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Headers 配置
+ *
+ * @author L.cm
+ */
+@Getter
+@Setter
+@ConfigurationProperties(BladeContextProperties.PREFIX)
+public class BladeContextProperties {
+	/**
+	 * 配置前缀
+	 */
+	public static final String PREFIX = "blade.context";
+	/**
+	 * 上下文传递的 headers 信息
+	 */
+	private Headers headers = new Headers();
+
+	@Getter
+	@Setter
+	public static class Headers {
+		/**
+		 * 请求id,默认:Blade-RequestId
+		 */
+		private String requestId = "Blade-RequestId";
+		/**
+		 * 用于 聚合层 向调用层传递用户信息 的请求头,默认:Blade-AccountId
+		 */
+		private String accountId = "Blade-AccountId";
+		/**
+		 * 用于 聚合层 向调用层传递租户id 的请求头,默认:Blade-TenantId
+		 */
+		private String tenantId = "Blade-TenantId";
+		/**
+		 * 自定义 RestTemplate 和 Feign 透传到下层的 Headers 名称列表
+		 */
+		private List<String> allowed = Arrays.asList("X-Real-IP", "x-forwarded-for", "authorization", "Authorization", TokenConstant.HEADER.toLowerCase(), TokenConstant.HEADER);
+	}
+
+	/**
+	 * 获取跨服务的请求头
+	 *
+	 * @return 请求头列表
+	 */
+	public List<String> getCrossHeaders() {
+		List<String> headerList = new ArrayList<>();
+		headerList.add(headers.getRequestId());
+		headerList.add(headers.getAccountId());
+		headerList.add(headers.getTenantId());
+		headerList.addAll(headers.getAllowed());
+		return headerList;
+	}
+
+}

+ 72 - 0
bladex-tool/blade-core-db/pom.xml

@@ -0,0 +1,72 @@
+<?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>BladeX-Tool</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.8.2.RELEASE</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-core-db</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${project.parent.version}</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <!--Blade-->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-tool</artifactId>
+        </dependency>
+        <!--Spring-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-jdbc</artifactId>
+            <exclusions>
+                <exclusion>
+                    <artifactId>tomcat-jdbc</artifactId>
+                    <groupId>org.apache.tomcat</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <!--Mybatis-->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+        </dependency>
+        <!-- Druid -->
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+        </dependency>
+        <!-- MySql -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <!-- Oracle -->
+        <dependency>
+            <groupId>com.oracle</groupId>
+            <artifactId>ojdbc7</artifactId>
+        </dependency>
+        <!-- PostgreSql -->
+        <dependency>
+            <groupId>org.postgresql</groupId>
+            <artifactId>postgresql</artifactId>
+        </dependency>
+        <!-- SqlServer -->
+        <dependency>
+            <groupId>com.microsoft.sqlserver</groupId>
+            <artifactId>mssql-jdbc</artifactId>
+        </dependency>
+        <!-- Auto -->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-auto</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>

+ 31 - 0
bladex-tool/blade-core-db/src/main/java/org/springblade/core/db/config/DbConfiguration.java

@@ -0,0 +1,31 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.db.config;
+
+import org.springblade.core.launch.props.BladePropertySource;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 数据源配置类
+ *
+ * @author Chill
+ */
+@Configuration(proxyBeanMethods = false)
+@BladePropertySource(value = "classpath:/blade-db.yml")
+public class DbConfiguration {
+
+}

+ 6 - 0
bladex-tool/blade-core-db/src/main/java/org/springblade/core/db/package-info.java

@@ -0,0 +1,6 @@
+/**
+ * Created by Blade.
+ *
+ * @author zhuangqian
+ */
+package org.springblade.core.db;

+ 39 - 0
bladex-tool/blade-core-db/src/main/resources/blade-db.yml

@@ -0,0 +1,39 @@
+#spring-datasource配置
+spring:
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    druid:
+      initial-size: 5
+      max-active: 20
+      min-idle: 5
+      max-wait: 60000
+      # MySql、PostgreSQL校验
+      validation-query: select 1
+      # Oracle校验
+      #validation-query: select 1 from dual
+      validation-query-timeout: 2000
+      test-on-borrow: false
+      test-on-return: false
+      test-while-idle: true
+      time-between-eviction-runs-millis: 60000
+      min-evictable-idle-time-millis: 300000
+      stat-view-servlet:
+        enabled: true
+        login-username: blade
+        login-password: 1qaz@WSX
+      web-stat-filter:
+        enabled: true
+        url-pattern: /*
+        exclusions: '*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*'
+        session-stat-enable: true
+        session-stat-max-count: 10
+      #hikari:
+      #connection-test-query: SELECT 1 FROM DUAL
+      #connection-timeout: 30000
+      #maximum-pool-size: 5
+      #max-lifetime: 1800000
+      #minimum-idle: 1
+
+
+
+

+ 42 - 0
bladex-tool/blade-core-launch/pom.xml

@@ -0,0 +1,42 @@
+<?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>BladeX-Tool</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.8.2.RELEASE</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-core-launch</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${project.parent.version}</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <!--Spring-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-tomcat</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-undertow</artifactId>
+        </dependency>
+        <!-- Auto -->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-auto</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+</project>

+ 129 - 0
bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/BladeApplication.java

@@ -0,0 +1,129 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.launch;
+
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.launch.constant.NacosConstant;
+import org.springblade.core.launch.service.LauncherService;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.core.env.*;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ * 项目启动器,搞定环境变量问题
+ *
+ * @author Chill
+ */
+public class BladeApplication {
+
+	/**
+	 * Create an application context
+	 * java -jar app.jar --spring.profiles.active=prod --server.port=2333
+	 *
+	 * @param appName application name
+	 * @param source  The sources
+	 * @return an application context created from the current state
+	 */
+	public static ConfigurableApplicationContext run(String appName, Class source, String... args) {
+		SpringApplicationBuilder builder = createSpringApplicationBuilder(appName, source, args);
+		return builder.run(args);
+	}
+
+	public static SpringApplicationBuilder createSpringApplicationBuilder(String appName, Class source, String... args) {
+		Assert.hasText(appName, "[appName]服务名不能为空");
+		// 读取环境变量,使用spring boot的规则
+		ConfigurableEnvironment environment = new StandardEnvironment();
+		MutablePropertySources propertySources = environment.getPropertySources();
+		propertySources.addFirst(new SimpleCommandLinePropertySource(args));
+		propertySources.addLast(new MapPropertySource(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, environment.getSystemProperties()));
+		propertySources.addLast(new SystemEnvironmentPropertySource(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, environment.getSystemEnvironment()));
+		// 获取配置的环境变量
+		String[] activeProfiles = environment.getActiveProfiles();
+		// 判断环境:dev、test、prod
+		List<String> profiles = Arrays.asList(activeProfiles);
+		// 预设的环境
+		List<String> presetProfiles = new ArrayList<>(Arrays.asList(AppConstant.DEV_CODE, AppConstant.TEST_CODE, AppConstant.PROD_CODE));
+		// 交集
+		presetProfiles.retainAll(profiles);
+		// 当前使用
+		List<String> activeProfileList = new ArrayList<>(profiles);
+		Function<Object[], String> joinFun = StringUtils::arrayToCommaDelimitedString;
+		SpringApplicationBuilder builder = new SpringApplicationBuilder(source);
+		String profile;
+		if (activeProfileList.isEmpty()) {
+			// 默认dev开发
+			profile = AppConstant.DEV_CODE;
+			activeProfileList.add(profile);
+			builder.profiles(profile);
+		} else if (activeProfileList.size() == 1) {
+			profile = activeProfileList.get(0);
+		} else {
+			// 同时存在dev、test、prod环境时
+			throw new RuntimeException("同时存在环境变量:[" + StringUtils.arrayToCommaDelimitedString(activeProfiles) + "]");
+		}
+		String startJarPath = BladeApplication.class.getResource("/").getPath().split("!")[0];
+		String activePros = joinFun.apply(activeProfileList.toArray());
+		System.out.printf("----启动中,读取到的环境变量:[%s],jar地址:[%s]----%n", activePros, startJarPath);
+		Properties props = System.getProperties();
+		props.setProperty("spring.application.name", appName);
+		props.setProperty("spring.profiles.active", profile);
+		props.setProperty("info.version", AppConstant.APPLICATION_VERSION);
+		props.setProperty("info.desc", appName);
+		props.setProperty("file.encoding", StandardCharsets.UTF_8.name());
+		props.setProperty("blade.env", profile);
+		props.setProperty("blade.name", appName);
+		props.setProperty("blade.is-local", String.valueOf(isLocalDev()));
+		props.setProperty("blade.dev-mode", profile.equals(AppConstant.PROD_CODE) ? "false" : "true");
+		props.setProperty("blade.service.version", AppConstant.APPLICATION_VERSION);
+		Properties defaultProperties = new Properties();
+		defaultProperties.setProperty("spring.main.allow-bean-definition-overriding", "true");
+		defaultProperties.setProperty("spring.sleuth.sampler.percentage", "1.0");
+		defaultProperties.setProperty("spring.cloud.alibaba.seata.tx-service-group", appName.concat(NacosConstant.NACOS_GROUP_SUFFIX));
+		defaultProperties.setProperty("spring.cloud.nacos.config.file-extension", NacosConstant.NACOS_CONFIG_FORMAT);
+		defaultProperties.setProperty("spring.cloud.nacos.config.shared-configs[0].data-id", NacosConstant.sharedDataId());
+		defaultProperties.setProperty("spring.cloud.nacos.config.shared-configs[0].group", NacosConstant.NACOS_CONFIG_GROUP);
+		defaultProperties.setProperty("spring.cloud.nacos.config.shared-configs[0].refresh", NacosConstant.NACOS_CONFIG_REFRESH);
+		defaultProperties.setProperty("spring.cloud.nacos.config.shared-configs[1].data-id", NacosConstant.sharedDataId(profile));
+		defaultProperties.setProperty("spring.cloud.nacos.config.shared-configs[1].group", NacosConstant.NACOS_CONFIG_GROUP);
+		defaultProperties.setProperty("spring.cloud.nacos.config.shared-configs[1].refresh", NacosConstant.NACOS_CONFIG_REFRESH);
+		builder.properties(defaultProperties);
+		// 加载自定义组件
+		List<LauncherService> launcherList = new ArrayList<>();
+		ServiceLoader.load(LauncherService.class).forEach(launcherList::add);
+		launcherList.stream().sorted(Comparator.comparing(LauncherService::getOrder)).collect(Collectors.toList())
+			.forEach(launcherService -> launcherService.launcher(builder, appName, profile, isLocalDev()));
+		return builder;
+	}
+
+	/**
+	 * 判断是否为本地开发环境
+	 *
+	 * @return boolean
+	 */
+	public static boolean isLocalDev() {
+		String osName = System.getProperty("os.name");
+		return StringUtils.hasText(osName) && !(AppConstant.OS_NAME_LINUX.equalsIgnoreCase(osName));
+	}
+
+}

+ 47 - 0
bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/StartEventListener.java

@@ -0,0 +1,47 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.launch;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.web.context.WebServerInitializedEvent;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.Order;
+import org.springframework.core.env.Environment;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.util.StringUtils;
+
+/**
+ * 项目启动事件通知
+ *
+ * @author Chill
+ */
+@Slf4j
+@Configuration(proxyBeanMethods = false)
+public class StartEventListener {
+
+	@Async
+	@Order
+	@EventListener(WebServerInitializedEvent.class)
+	public void afterStart(WebServerInitializedEvent event) {
+		Environment environment = event.getApplicationContext().getEnvironment();
+		String appName = environment.getProperty("spring.application.name").toUpperCase();
+		int localPort = event.getWebServer().getPort();
+		String profile = StringUtils.arrayToCommaDelimitedString(environment.getActiveProfiles());
+		log.info("---[{}]---启动完成,当前使用的端口:[{}],环境变量:[{}]---", appName, localPort, profile);
+	}
+}

+ 34 - 0
bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/config/BladeLaunchConfiguration.java

@@ -0,0 +1,34 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.launch.config;
+
+import lombok.AllArgsConstructor;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+
+/**
+ * 配置类
+ *
+ * @author Chill
+ */
+@Configuration(proxyBeanMethods = false)
+@AllArgsConstructor
+@Order(Ordered.HIGHEST_PRECEDENCE)
+public class BladeLaunchConfiguration {
+
+}

+ 43 - 0
bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/config/BladePropertyConfiguration.java

@@ -0,0 +1,43 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+
+package org.springblade.core.launch.config;
+
+import org.springblade.core.launch.props.BladePropertySourcePostProcessor;
+import org.springblade.core.launch.props.BladeProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+
+/**
+ * blade property config
+ *
+ * @author L.cm
+ */
+@Configuration(proxyBeanMethods = false)
+@Order(Ordered.HIGHEST_PRECEDENCE)
+@EnableConfigurationProperties(BladeProperties.class)
+public class BladePropertyConfiguration {
+
+	@Bean
+	public BladePropertySourcePostProcessor bladePropertySourcePostProcessor() {
+		return new BladePropertySourcePostProcessor();
+	}
+
+}

+ 131 - 0
bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/constant/AppConstant.java

@@ -0,0 +1,131 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.launch.constant;
+
+/**
+ * 系统常量
+ *
+ * @author Chill
+ */
+public interface AppConstant {
+
+	/**
+	 * 应用版本
+	 */
+	String APPLICATION_VERSION = "2.8.2.RELEASE";
+
+	/**
+	 * 基础包
+	 */
+	String BASE_PACKAGES = "org.springblade";
+
+	/**
+	 * 应用名前缀
+	 */
+	String APPLICATION_NAME_PREFIX = "blade-";
+	/**
+	 * 网关模块名称
+	 */
+	String APPLICATION_GATEWAY_NAME = APPLICATION_NAME_PREFIX + "gateway";
+	/**
+	 * 授权模块名称
+	 */
+	String APPLICATION_AUTH_NAME = APPLICATION_NAME_PREFIX + "auth";
+	/**
+	 * 监控模块名称
+	 */
+	String APPLICATION_ADMIN_NAME = APPLICATION_NAME_PREFIX + "admin";
+	/**
+	 * 报表系统名称
+	 */
+	String APPLICATION_REPORT_NAME = APPLICATION_NAME_PREFIX + "report";
+	/**
+	 * 集群监控名称
+	 */
+	String APPLICATION_TURBINE_NAME = APPLICATION_NAME_PREFIX + "turbine";
+	/**
+	 * 链路追踪名称
+	 */
+	String APPLICATION_ZIPKIN_NAME = APPLICATION_NAME_PREFIX + "zipkin";
+	/**
+	 * websocket名称
+	 */
+	String APPLICATION_WEBSOCKET_NAME = APPLICATION_NAME_PREFIX + "websocket";
+	/**
+	 * 首页模块名称
+	 */
+	String APPLICATION_DESK_NAME = APPLICATION_NAME_PREFIX + "desk";
+	/**
+	 * 系统模块名称
+	 */
+	String APPLICATION_SYSTEM_NAME = APPLICATION_NAME_PREFIX + "system";
+	/**
+	 * 用户模块名称
+	 */
+	String APPLICATION_USER_NAME = APPLICATION_NAME_PREFIX + "user";
+	/**
+	 * 日志模块名称
+	 */
+	String APPLICATION_LOG_NAME = APPLICATION_NAME_PREFIX + "log";
+	/**
+	 * 开发模块名称
+	 */
+	String APPLICATION_DEVELOP_NAME = APPLICATION_NAME_PREFIX + "develop";
+	/**
+	 * 流程设计器模块名称
+	 */
+	String APPLICATION_FLOWDESIGN_NAME = APPLICATION_NAME_PREFIX + "flowdesign";
+	/**
+	 * 工作流模块名称
+	 */
+	String APPLICATION_FLOW_NAME = APPLICATION_NAME_PREFIX + "flow";
+	/**
+	 * 资源模块名称
+	 */
+	String APPLICATION_RESOURCE_NAME = APPLICATION_NAME_PREFIX + "resource";
+	/**
+	 * 接口文档模块名称
+	 */
+	String APPLICATION_SWAGGER_NAME = APPLICATION_NAME_PREFIX + "swagger";
+	/**
+	 * 测试模块名称
+	 */
+	String APPLICATION_TEST_NAME = APPLICATION_NAME_PREFIX + "test";
+	/**
+	 * 演示模块名称
+	 */
+	String APPLICATION_DEMO_NAME = APPLICATION_NAME_PREFIX + "demo";
+
+	/**
+	 * 开发环境
+	 */
+	String DEV_CODE = "dev";
+	/**
+	 * 生产环境
+	 */
+	String PROD_CODE = "prod";
+	/**
+	 * 测试环境
+	 */
+	String TEST_CODE = "test";
+
+	/**
+	 * 代码部署于 linux 上,工作默认为 mac 和 Windows
+	 */
+	String OS_NAME_LINUX = "LINUX";
+
+}

+ 50 - 0
bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/constant/ConsulConstant.java

@@ -0,0 +1,50 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.launch.constant;
+
+/**
+ * Consul常量.
+ *
+ * @author Chill
+ */
+public interface ConsulConstant {
+
+	/**
+	 * consul dev 地址
+	 */
+	String CONSUL_HOST = "http://localhost";
+
+	/**
+	 * consul端口
+	 */
+	String CONSUL_PORT = "8500";
+
+	/**
+	 * consul端口
+	 */
+	String CONSUL_CONFIG_FORMAT = "yaml";
+
+	/**
+	 * consul端口
+	 */
+	String CONSUL_WATCH_DELAY = "1000";
+
+	/**
+	 * consul端口
+	 */
+	String CONSUL_WATCH_ENABLED = "true";
+}

+ 31 - 0
bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/constant/FlowConstant.java

@@ -0,0 +1,31 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.launch.constant;
+
+/**
+ * 流程常量.
+ *
+ * @author Chill
+ */
+public interface FlowConstant {
+
+	/**
+	 * 任务用户标识前缀
+	 */
+	String TASK_USR_PREFIX = "taskUser_";
+
+}

+ 128 - 0
bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/constant/NacosConstant.java

@@ -0,0 +1,128 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.launch.constant;
+
+/**
+ * Nacos常量.
+ *
+ * @author Chill
+ */
+public interface NacosConstant {
+
+	/**
+	 * nacos 地址
+	 */
+	String NACOS_ADDR = "127.0.0.1:8848";
+
+	/**
+	 * nacos 配置前缀
+	 */
+	String NACOS_CONFIG_PREFIX = "blade";
+
+	/**
+	 * nacos 组配置后缀
+	 */
+	String NACOS_GROUP_SUFFIX = "-group";
+
+	/**
+	 * nacos 配置文件类型
+	 */
+	String NACOS_CONFIG_FORMAT = "yaml";
+
+	/**
+	 * nacos json配置文件类型
+	 */
+	String NACOS_CONFIG_JSON_FORMAT = "json";
+
+	/**
+	 * nacos 是否刷新
+	 */
+	String NACOS_CONFIG_REFRESH = "true";
+
+	/**
+	 * nacos 分组
+	 */
+	String NACOS_CONFIG_GROUP = "DEFAULT_GROUP";
+
+	/**
+	 * seata 分组
+	 */
+	String NACOS_SEATA_GROUP = "SEATA_GROUP";
+
+	/**
+	 * 构建服务对应的 dataId
+	 *
+	 * @param appName 服务名
+	 * @return dataId
+	 */
+	static String dataId(String appName) {
+		return appName + "." + NACOS_CONFIG_FORMAT;
+	}
+
+	/**
+	 * 构建服务对应的 dataId
+	 *
+	 * @param appName 服务名
+	 * @param profile 环境变量
+	 * @return dataId
+	 */
+	static String dataId(String appName, String profile) {
+		return dataId(appName, profile, NACOS_CONFIG_FORMAT);
+	}
+
+	/**
+	 * 构建服务对应的 dataId
+	 *
+	 * @param appName 服务名
+	 * @param profile 环境变量
+	 * @param format  文件类型
+	 * @return dataId
+	 */
+	static String dataId(String appName, String profile, String format) {
+		return appName + "-" + profile + "." + format;
+	}
+
+	/**
+	 * 服务默认加载的配置
+	 *
+	 * @return sharedDataIds
+	 */
+	static String sharedDataId() {
+		return NACOS_CONFIG_PREFIX + "." + NACOS_CONFIG_FORMAT;
+	}
+
+	/**
+	 * 服务默认加载的配置
+	 *
+	 * @param profile 环境变量
+	 * @return sharedDataIds
+	 */
+	static String sharedDataId(String profile) {
+		return NACOS_CONFIG_PREFIX + "-" + profile + "." + NACOS_CONFIG_FORMAT;
+	}
+
+	/**
+	 * 服务默认加载的配置
+	 *
+	 * @param profile 环境变量
+	 * @return sharedDataIds
+	 */
+	static String sharedDataIds(String profile) {
+		return NACOS_CONFIG_PREFIX + "." + NACOS_CONFIG_FORMAT + "," + NACOS_CONFIG_PREFIX + "-" + profile + "." + NACOS_CONFIG_FORMAT;
+	}
+
+}

+ 30 - 0
bladex-tool/blade-core-launch/src/main/java/org/springblade/core/launch/constant/SentinelConstant.java

@@ -0,0 +1,30 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.core.launch.constant;
+
+/**
+ * sentinel配置.
+ *
+ * @author Chill
+ */
+public interface SentinelConstant {
+
+	/**
+	 * sentinel 地址
+	 */
+	String SENTINEL_ADDR = "127.0.0.1:8858";
+}

Некоторые файлы не были показаны из-за большого количества измененных файлов