diff --git a/README.md b/README.md
index dc3dfc0..5424e7a 100644
--- a/README.md
+++ b/README.md
@@ -1,79 +1,98 @@
>转载请标明出处:
-> https://blog.csdn.net/u010562966/article/details/88995076
-> 本文出自[程序员果果的博客](https://blog.csdn.net/u010562966)
->
+> http://blog.itwolfed.com/
+> 本文出自[程序员果果的博客](http://blog.itwolfed.com/)
### 源码
- github | Spring Boot 系列 :[https://github.com/gf-huanchupk/SpringBootLearning](https://github.com/gf-huanchupk/SpringBootLearning),记得star哦!
- github | Spring Cloud 系列 :[https://github.com/gf-huanchupk/SpringCloudLearning](https://github.com/gf-huanchupk/SpringCloudLearning),记得star哦!
+### RocketMQ 系列
+- [RocketMQ 教程 | RocketMQ 简介](http://blog.itwolfed.com/blog/98)
+- [RocketMQ 教程 | RocketMQ 安装](http://blog.itwolfed.com/blog/99)
+- [RocketMQ 教程 | RocketMQ 常用消息类型](http://blog.itwolfed.com/blog/101)
### Spring Boot 系列
-- [Spring Boot 教程 | Spring Boot 自定义 starter](https://mp.weixin.qq.com/s/F_1j-ng49QNlbj04Q9bqFQ)
-- [Spring Boot 教程 | Spring Boot 整合 mybatis-plus](https://mp.weixin.qq.com/s/ITZIPCttey-iYCyhIa8uSw)
-- [Spring Boot 教程 | Spring Boot 整合 spring cache](https://mp.weixin.qq.com/s/nqozEsvl5ZMaREMv2gZKlQ)
-- [Spring Boot 教程 | Spring Boot 整合 rabbitmq](https://mp.weixin.qq.com/s/ZhP8zF3GyiIm0fPiTDLWxA)
-- [Spring Boot 教程 | Spring Boot 整合 elasticsearch](https://mp.weixin.qq.com/s/f9eE7jsPNWAr1gQ0iF-kDw)
-- [Spring Boot 教程 | Spring Boot 整合 docker](https://mp.weixin.qq.com/s/IWDpC9i8P3ZDFDYTsKARag)
-- [Spring Boot 教程 | Spring Boot 整合 elk](https://mp.weixin.qq.com/s/k6UUCD-Rn96po2dw6maSxQ)
-- [Spring Boot 教程 | Spring Boot Admin 2.0 详解](https://mp.weixin.qq.com/s/JSJH32N_bCzaWjZTubUDTA)
-- [Spring Boot 教程 | Spring Boot 整合 apollo](https://mp.weixin.qq.com/s/8UR13rTY2elAGedfFC8EiQ)
-- [Spring Boot 教程 | Spring Boot Security 详解](https://mp.weixin.qq.com/s/bSWs_3wIavdhHayDtKHpUg)
-- [Spring Boot 教程 | Spring Boot Security 整合 OAuth2 设计安全API接口服务](https://mp.weixin.qq.com/s/0PAUErDh0qmcR4SUsTn15Q)
-- [Spring Boot 教程 | Spring Boot Security 整合 JWT 实现 无状态的分布式API接口](https://mp.weixin.qq.com/s/QO5L1-RCR-jmIuHlZIfkqQ)
+- [Spring Boot 教程 | Spring Boot 自定义 starter](http://blog.itwolfed.com/blog/5)
+- [Spring Boot 教程 | Spring Boot 整合 mybatis-plus](http://blog.itwolfed.com/blog/6)
+- [Spring Boot 教程 | Spring Boot 整合 spring cache](http://blog.itwolfed.com/blog/7)
+- [Spring Boot 教程 | Spring Boot 整合 rabbitmq](http://blog.itwolfed.com/blog/8)
+- [Spring Boot 教程 | Spring Boot 整合 elasticsearch](http://blog.itwolfed.com/blog/9)
+- [Spring Boot 教程 | Spring Boot 整合 docker](http://blog.itwolfed.com/blog/10)
+- [Spring Boot 教程 | Spring Boot 整合 elk](http://blog.itwolfed.com/blog/11)
+- [Spring Boot 教程 | Spring Boot Admin 2.0 详解](http://blog.itwolfed.com/blog/12)
+- [Spring Boot 教程 | Spring Boot 整合 apollo](http://blog.itwolfed.com/blog/13)
+- [Spring Boot 教程 | Spring Boot Security 详解](http://blog.itwolfed.com/blog/14)
+- [Spring Boot 教程 | Spring Boot Security 整合 OAuth2 设计安全API接口服务](http://blog.itwolfed.com/blog/15)
+- [Spring Boot 教程 | Spring Boot Security 整合 JWT 实现 无状态的分布式API接口](http://blog.itwolfed.com/blog/16)
+- [Spring Boot 教程 | Spring Boot Security OAuth2 实现支持JWT令牌的授权服务器](http://blog.itwolfed.com/blog/17)
+- [Spring Boot 教程 | Spring Boot Actuator 整合 Prometheus](http://blog.itwolfed.com/blog/18)
+- [Spring Boot 教程 | Spring Boot 集成 Seata 解决分布式事务问题](http://blog.itwolfed.com/blog/19)
+- [Spring Boot 教程 | Spring Boot 使用 JSR303 实现参数验证](http://blog.itwolfed.com/blog/97)
### Spring Cloud 系列
-- [第一篇 : 服务的注册与发现(Eureka)](https://mp.weixin.qq.com/s/Ahg6Z5PL9UAhaKSQjUOg3g)
-- [第二篇 : 服务消费者(rest+ribbon)](https://mp.weixin.qq.com/s/LanJnx04KaAuSkeomaE5Uw)
-- [第三篇 : 服务消费者(Feign)](https://mp.weixin.qq.com/s/2_OxTx1_6PfQx3z7baxocw)
-- [第四篇 : 断路器(Hystrix)](https://mp.weixin.qq.com/s/XhjHZ9fD2s34Eb665O6npQ)
-- [第五篇 : 路由网关(Zuul)](https://mp.weixin.qq.com/s/GYu_BRxuhmGl7dQnlnsEmQ)
-- [第六篇 : 分布式配置中心(Spring Cloud Config)](https://mp.weixin.qq.com/s/Pbcc3B3vzrMUKgbpijrJFQ)
-- [第七篇 : 高可用的分布式配置中心(Spring Cloud Config)](https://mp.weixin.qq.com/s/yj0snlPFobakq3akc_FJGw)
-- [第八篇 : 消息总线(Spring Cloud Bus) 8 months ago](https://mp.weixin.qq.com/s/a2NLClc56BXJxrIiCXODUw)
-- [第九篇 : 服务链路追踪(Spring Cloud Sleuth)](https://mp.weixin.qq.com/s/a2NLClc56BXJxrIiCXODUw)
-- [第十篇 : 高可用的服务注册中心](https://mp.weixin.qq.com/s/A75d2Ec5-IwoGxbJEWj41w)
-- [第十一篇 : 断路器监控(Hystrix Dashboard)](https://mp.weixin.qq.com/s/Sv7agttPg2R1kyXMvxI-Gw)
-- [第十二篇 : 断路器聚合监控(Hystrix Turbine)](https://mp.weixin.qq.com/s/KyAdC-k_r9ldyozKmnyK3Q)
-- [第十三篇 : 路由网关(Gateway)](https://mp.weixin.qq.com/s/2twx-aCEM2GXluDUIubGZg)
-- [第十四篇 : 调用链监控 CAT 之 入门](https://mp.weixin.qq.com/s/G15cw9yXpq4cOFB2C29JYw)
-- [第十五篇 : 调用链监控 CAT 之 URL埋点实践](https://mp.weixin.qq.com/s/0tLjjlUtKTyOlH-5elj0Xw)
-- [第十六篇 : 微服务容错限流Hystrix入门](https://mp.weixin.qq.com/s/G15cw9yXpq4cOFB2C29JYw)
-- [第十七篇 : 微服务容错限流Hystrix之Dashboard](https://mp.weixin.qq.com/s/Nmqu2ul4aRT6iXecxrmt9A)
-- [第十八篇 : 微服务容错限流Hystrix之流聚合](https://mp.weixin.qq.com/s/2RNK2XkjTvd1sUM7oR-Jfw)
+- [第一篇 : 服务的注册与发现(Eureka)](http://blog.itwolfed.com/blog/20)
+- [第二篇 : 服务消费者(rest+ribbon)](http://blog.itwolfed.com/blog/21)
+- [第三篇 : 服务消费者(Feign)](http://blog.itwolfed.com/blog/22)
+- [第四篇 : 断路器(Hystrix)](http://blog.itwolfed.com/blog/23)
+- [第五篇 : 路由网关(Zuul)](http://blog.itwolfed.com/blog/24)
+- [第六篇 : 分布式配置中心(Spring Cloud Config)](http://blog.itwolfed.com/blog/25)
+- [第七篇 : 高可用的分布式配置中心(Spring Cloud Config)](http://blog.itwolfed.com/blog/26)
+- [第八篇 : 消息总线(Spring Cloud Bus)](http://blog.itwolfed.com/blog/27)
+- [第九篇 : 服务链路追踪(Spring Cloud Sleuth)](http://blog.itwolfed.com/blog/28)
+- [第十篇 : 高可用的服务注册中心](http://blog.itwolfed.com/blog/29)
+- [第十一篇 : 断路器监控(Hystrix Dashboard)](http://blog.itwolfed.com/blog/30)
+- [第十二篇 : 断路器聚合监控(Hystrix Turbine)](http://blog.itwolfed.com/blog/31)
+- [第十三篇 : 路由网关(Gateway)](http://blog.itwolfed.com/blog/32)
+- [第十四篇 : 调用链监控 CAT 之 入门](http://blog.itwolfed.com/blog/37)
+- [第十五篇 : 调用链监控 CAT 之 URL埋点实践](http://blog.itwolfed.com/blog/38)
+- [第十六篇 : 微服务容错限流Hystrix入门](http://blog.itwolfed.com/blog/39)
+- [第十七篇 : 微服务容错限流Hystrix之Dashboard](http://blog.itwolfed.com/blog/40)
+- [第十八篇 : 微服务容错限流Hystrix之流聚合](http://blog.itwolfed.com/blog/41)
+
+### Spring Cloud Gateway 系列
+- [Spring Cloud Gateway 入门](http://blog.itwolfed.com/blog/32)
+- [Spring Cloud Gateway 之 Predict](http://blog.itwolfed.com/blog/33)
+- [Spring Cloud Gateway 之 Filter](http://blog.itwolfed.com/blog/34)
+- [Spring Cloud Gateway 之 限流](http://blog.itwolfed.com/blog/35)
+- [Spring Cloud Gateway 之 服务注册与发现](http://blog.itwolfed.com/blog/36)
+
+### Spring Cloud 源码解析
+- [Spring Cloud 源码 : 客户端负载均衡 Ribbon 源码解析](http://blog.itwolfed.com/blog/42)
+- [Spring Cloud 源码 : Eureka 源码分析之 Eureka Client](http://blog.itwolfed.com/blog/43)
+- [Spring Cloud 源码 : Eureka 源码分析之 Eureka Server](http://blog.itwolfed.com/blog/44)
### Dubbo 系列
-- [Dubbo 教程 | 第一篇 : Dubbo 搭建管理控制台](https://mp.weixin.qq.com/s/UYcmklod_2RLrx-ACHPyVw)
-- [Dubbo 教程 | 第二篇 : Dubbo 创建 提供者 消费者](https://mp.weixin.qq.com/s/2CRFwbc45oh5Nqd7eREAAg)
-- [Dubbo 教程 | 第三篇 : Dubbo 配置](https://mp.weixin.qq.com/s/Dld2DYSbSXmIQxFVE31RNQ)
-- [Dubbo 教程 | 第四篇 : Dubbo 高可用](https://mp.weixin.qq.com/s/f0vsJ8jcF24ji5UNlCq1zQ)
+- [Dubbo 教程 | 第一篇 : Dubbo 搭建管理控制台](http://blog.itwolfed.com/blog/82)
+- [Dubbo 教程 | 第二篇 : Dubbo 创建 提供者 消费者](http://blog.itwolfed.com/blog/83)
+- [Dubbo 教程 | 第三篇 : Dubbo 配置](http://blog.itwolfed.com/blog/84)
+- [Dubbo 教程 | 第四篇 : Dubbo 高可用](http://blog.itwolfed.com/blog/85)
### Docker 系列
-- [Docker 教程 | 第一篇 : Docker的基本概念和框架](https://mp.weixin.qq.com/s/8VM-c_UkxYcVw2Itiapw4w)
-- [Docker 教程 | 第二篇 : Docker 常用操作命令](https://mp.weixin.qq.com/s/S9VkzSJx_JOY8zDiC_nuEg)
-- [Docker 教程 | 第三篇 : Docker 获取和推送镜像](https://mp.weixin.qq.com/s/jN-8YgxDcAvwoiZ_BJQUKQ)
-- [Docker 教程 | 第四篇 : Docker 构建镜像](https://mp.weixin.qq.com/s/foVeANf0imgU4hfOempp3A)
-- [Docker 教程 | 第五篇 : Docker 容器的网络连接](https://mp.weixin.qq.com/s/DzF-ZwaY4QtlgM32I5wybg)
-- [Docker 教程 | 第六篇 : Docker 容器的数据管理](https://mp.weixin.qq.com/s/qg8eXHobNxzfPAaHB3U9cw)
-- [Docker 教程 | 第七篇 : Docker 私有仓库搭建](https://mp.weixin.qq.com/s/tBh6kT4I5XniCoFEcn7W9A)
-- [Docker 教程 | 第八篇 : 服务 Docker 化](https://mp.weixin.qq.com/s/E_gJFbRaWOE-mnVR1lsYfQ)
-- [Docker 教程 | 第九篇 : Docker Swarm 集群环境搭建](https://mp.weixin.qq.com/s/RSNL-V4jLE8YVMSWZlJ_Vw)
-- [Docker 教程 | 第十篇 : Docker Swarm 微服务部署](https://mp.weixin.qq.com/s/nWpbqAheuTh45dWsgszieA)
+- [Docker 教程 | 第一篇 : Docker的基本概念和框架](http://blog.itwolfed.com/blog/86)
+- [Docker 教程 | 第二篇 : Docker 常用操作命令](http://blog.itwolfed.com/blog/87)
+- [Docker 教程 | 第三篇 : Docker 获取和推送镜像](http://blog.itwolfed.com/blog/88)
+- [Docker 教程 | 第四篇 : Docker 构建镜像](http://blog.itwolfed.com/blog/89)
+- [Docker 教程 | 第五篇 : Docker 容器的网络连接](http://blog.itwolfed.com/blog/90)
+- [Docker 教程 | 第六篇 : Docker 容器的数据管理](http://blog.itwolfed.com/blog/91)
+- [Docker 教程 | 第七篇 : Docker 私有仓库搭建](http://blog.itwolfed.com/blog/92)
+- [Docker 教程 | 第八篇 : 服务 Docker 化](http://blog.itwolfed.com/blog/93)
+- [Docker 教程 | 第九篇 : Docker Swarm 集群环境搭建](http://blog.itwolfed.com/blog/94)
+- [Docker 教程 | 第十篇 : Docker Swarm 微服务部署](http://blog.itwolfed.com/blog/95)
### 关注我
-
+
欢迎扫码或微信搜索公众号《程序员果果》关注我,更多精彩内容不能错过 ~~
- (转载本站文章请注明作者和出处 程序员果果的博客)
+ (转载本站文章请注明作者和出处 程序员果果的博客)
diff --git a/springboot-actuator-prometheus/pom.xml b/springboot-actuator-prometheus/pom.xml
new file mode 100755
index 0000000..7e7e42e
--- /dev/null
+++ b/springboot-actuator-prometheus/pom.xml
@@ -0,0 +1,51 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.9.RELEASE
+
+
+ com.gf
+ springboot-actuator-prometheus
+ 0.0.1-SNAPSHOT
+ springboot-actuator-prometheus
+ Demo project for Spring Boot
+
+
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ io.micrometer
+ micrometer-registry-prometheus
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/springboot-actuator-prometheus/src/main/java/com/gf/SpringbootActuatorPrometheusDemoApplication.java b/springboot-actuator-prometheus/src/main/java/com/gf/SpringbootActuatorPrometheusDemoApplication.java
new file mode 100755
index 0000000..7eed731
--- /dev/null
+++ b/springboot-actuator-prometheus/src/main/java/com/gf/SpringbootActuatorPrometheusDemoApplication.java
@@ -0,0 +1,42 @@
+package com.gf;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+
+@SpringBootApplication
+@RestController
+public class SpringbootActuatorPrometheusDemoApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SpringbootActuatorPrometheusDemoApplication.class, args);
+ }
+
+ @RequestMapping(value = "/hello")
+ public String sayHello() {
+ for (int i = 1 ; i <= 10 ; i++) {
+ Thread t = new Thread(() -> {
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ } , "HelloThread - " + i);
+ t.start();
+ }
+ return "ok";
+ }
+
+ /**
+ @Bean
+ MeterRegistryCustomizer metricsCommonTags() {
+ return registry -> registry.config().commonTags("application", "springboot-actuator-prometheus-demo");
+ }
+ */
+
+}
diff --git a/springboot-actuator-prometheus/src/main/resources/application.yml b/springboot-actuator-prometheus/src/main/resources/application.yml
new file mode 100755
index 0000000..9e23bc6
--- /dev/null
+++ b/springboot-actuator-prometheus/src/main/resources/application.yml
@@ -0,0 +1,14 @@
+management:
+ endpoints:
+ web:
+ exposure:
+ include: '*'
+
+ endpoint:
+ health:
+ show-details: always
+
+ metrics:
+ tags:
+ application: actuator-demo
+
diff --git a/springboot-actuator-prometheus/src/test/java/com/gf/SpringbootActuatorPrometheusDemoApplicationTests.java b/springboot-actuator-prometheus/src/test/java/com/gf/SpringbootActuatorPrometheusDemoApplicationTests.java
new file mode 100755
index 0000000..8b7c640
--- /dev/null
+++ b/springboot-actuator-prometheus/src/test/java/com/gf/SpringbootActuatorPrometheusDemoApplicationTests.java
@@ -0,0 +1,16 @@
+package com.gf;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class SpringbootActuatorPrometheusDemoApplicationTests {
+
+ @Test
+ public void contextLoads() {
+ }
+
+}
diff --git a/springboot-jsr303/.gitignore b/springboot-jsr303/.gitignore
new file mode 100644
index 0000000..a2a3040
--- /dev/null
+++ b/springboot-jsr303/.gitignore
@@ -0,0 +1,31 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/
diff --git a/springboot-jsr303/.mvn/wrapper/MavenWrapperDownloader.java b/springboot-jsr303/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 0000000..24c6680
--- /dev/null
+++ b/springboot-jsr303/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2007-present 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.
+ */
+
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ private static final String WRAPPER_VERSION = "0.5.6";
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println( "- Downloader started" );
+ File baseDirectory = new File( args[0] );
+ System.out.println( "- Using base directory: " + baseDirectory.getAbsolutePath() );
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File( baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH );
+ String url = DEFAULT_DOWNLOAD_URL;
+ if (mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream( mavenWrapperPropertyFile );
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load( mavenWrapperPropertyFileInputStream );
+ url = mavenWrapperProperties.getProperty( PROPERTY_NAME_WRAPPER_URL, url );
+ } catch (IOException e) {
+ System.out.println( "- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'" );
+ } finally {
+ try {
+ if (mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println( "- Downloading from: " + url );
+
+ File outputFile = new File( baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH );
+ if (!outputFile.getParentFile().exists()) {
+ if (!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'" );
+ }
+ }
+ System.out.println( "- Downloading to: " + outputFile.getAbsolutePath() );
+ try {
+ downloadFileFromURL( url, outputFile );
+ System.out.println( "Done" );
+ System.exit( 0 );
+ } catch (Throwable e) {
+ System.out.println( "- Error downloading" );
+ e.printStackTrace();
+ System.exit( 1 );
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ if (System.getenv( "MVNW_USERNAME" ) != null && System.getenv( "MVNW_PASSWORD" ) != null) {
+ String username = System.getenv( "MVNW_USERNAME" );
+ char[] password = System.getenv( "MVNW_PASSWORD" ).toCharArray();
+ Authenticator.setDefault( new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication( username, password );
+ }
+ } );
+ }
+ URL website = new URL( urlString );
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel( website.openStream() );
+ FileOutputStream fos = new FileOutputStream( destination );
+ fos.getChannel().transferFrom( rbc, 0, Long.MAX_VALUE );
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/springboot-jsr303/.mvn/wrapper/maven-wrapper.jar b/springboot-jsr303/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000..2cc7d4a
Binary files /dev/null and b/springboot-jsr303/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/springboot-jsr303/.mvn/wrapper/maven-wrapper.properties b/springboot-jsr303/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000..642d572
--- /dev/null
+++ b/springboot-jsr303/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/springboot-jsr303/pom.xml b/springboot-jsr303/pom.xml
new file mode 100644
index 0000000..e42f567
--- /dev/null
+++ b/springboot-jsr303/pom.xml
@@ -0,0 +1,57 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.3.0.RELEASE
+
+
+ com.itwolfed
+ springboot-jsr303
+ 0.0.1-SNAPSHOT
+ springboot-jsr303
+ Demo project for Spring Boot
+
+
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/springboot-jsr303/src/main/java/com/itwolfed/SpringbootJsr303Application.java b/springboot-jsr303/src/main/java/com/itwolfed/SpringbootJsr303Application.java
new file mode 100644
index 0000000..c45a832
--- /dev/null
+++ b/springboot-jsr303/src/main/java/com/itwolfed/SpringbootJsr303Application.java
@@ -0,0 +1,13 @@
+package com.itwolfed;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SpringbootJsr303Application {
+
+ public static void main(String[] args) {
+ SpringApplication.run( SpringbootJsr303Application.class, args );
+ }
+
+}
diff --git a/springboot-jsr303/src/main/java/com/itwolfed/controller/UserController.java b/springboot-jsr303/src/main/java/com/itwolfed/controller/UserController.java
new file mode 100644
index 0000000..a3832ab
--- /dev/null
+++ b/springboot-jsr303/src/main/java/com/itwolfed/controller/UserController.java
@@ -0,0 +1,21 @@
+package com.itwolfed.controller;
+
+import com.itwolfed.entity.User;
+import com.itwolfed.utils.Result;
+import com.itwolfed.valid.Groups;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+
+@RestController
+@RequestMapping("/user")
+public class UserController {
+
+ @PostMapping("")
+ public Result save (@Validated(Groups.Add.class) User user) {
+ return Result.ok();
+ }
+
+}
diff --git a/springboot-jsr303/src/main/java/com/itwolfed/entity/User.java b/springboot-jsr303/src/main/java/com/itwolfed/entity/User.java
new file mode 100644
index 0000000..3de6ffe
--- /dev/null
+++ b/springboot-jsr303/src/main/java/com/itwolfed/entity/User.java
@@ -0,0 +1,30 @@
+package com.itwolfed.entity;
+
+import com.itwolfed.valid.Groups;
+import com.itwolfed.valid.ListValue;
+import lombok.Data;
+
+import javax.validation.constraints.Email;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Null;
+import javax.validation.constraints.Pattern;
+
+
+@Data
+public class User {
+
+ @Null(message = "新增不需要指定id" , groups = Groups.Add.class)
+ @NotNull(message = "修改需要指定id" , groups = Groups.Update.class)
+ private Integer id;
+ @NotBlank(message = "用户名不能为空")
+ @NotNull
+ private String username;
+ @Pattern(regexp = "^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$", message = "密码必须为8~16个字母和数字组合")
+ private String password;
+ @Email
+ private String email;
+ @ListValue( message = "性别应指定相应的值" , vals = {1,2} , groups = {Groups.Add.class , Groups.Update.class})
+ private Integer gender;
+
+}
diff --git a/springboot-jsr303/src/main/java/com/itwolfed/exception/GlobalExceptionControllerAdvice.java b/springboot-jsr303/src/main/java/com/itwolfed/exception/GlobalExceptionControllerAdvice.java
new file mode 100644
index 0000000..03abdd3
--- /dev/null
+++ b/springboot-jsr303/src/main/java/com/itwolfed/exception/GlobalExceptionControllerAdvice.java
@@ -0,0 +1,36 @@
+package com.itwolfed.exception;
+
+import com.itwolfed.utils.Result;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.BindException;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 统一异常处理
+ */
+@Slf4j
+@RestControllerAdvice(basePackages = "com.itwolfed.controller")
+public class GlobalExceptionControllerAdvice {
+
+ @ExceptionHandler(value= {MethodArgumentNotValidException.class , BindException.class})
+ public Result handleVaildException(Exception e){
+ BindingResult bindingResult = null;
+ if (e instanceof MethodArgumentNotValidException) {
+ bindingResult = ((MethodArgumentNotValidException)e).getBindingResult();
+ } else if (e instanceof BindException) {
+ bindingResult = ((BindException)e).getBindingResult();
+ }
+ Map errorMap = new HashMap<>(16);
+ bindingResult.getFieldErrors().forEach((fieldError)->
+ errorMap.put(fieldError.getField(),fieldError.getDefaultMessage())
+ );
+ return Result.build(400 , "非法参数 !" , errorMap);
+ }
+
+}
diff --git a/springboot-jsr303/src/main/java/com/itwolfed/utils/Result.java b/springboot-jsr303/src/main/java/com/itwolfed/utils/Result.java
new file mode 100644
index 0000000..fb0600f
--- /dev/null
+++ b/springboot-jsr303/src/main/java/com/itwolfed/utils/Result.java
@@ -0,0 +1,63 @@
+package com.itwolfed.utils;
+
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 自定义响应结构
+ */
+@Getter
+@Setter
+public class Result {
+
+ // 响应业务状态
+ private Integer status;
+
+ // 响应消息
+ private String msg;
+
+ // 响应中的数据
+ private Object data;
+
+
+ public Result() {
+
+ }
+
+ public static Result ok(Object data) {
+
+ return new Result(200 , data);
+ }
+
+ public static Result ok() {
+ return new Result(200 , null);
+ }
+
+ public static Result build(Integer status, String msg) {
+ return new Result(status, msg, null);
+ }
+
+
+ public static Result build(Integer status, String msg , Object data) {
+ return new Result(status,msg,data);
+ }
+
+ public Result(Integer status,String msg) {
+ this.status = status;
+ this.msg = msg;
+ }
+
+
+ public Result(Integer status,Object data) {
+ this.status = status;
+ this.data = data;
+ }
+
+ public Result(Integer status, String msg, Object data) {
+ this.status = status;
+ this.msg = msg;
+ this.data = data;
+ }
+
+}
\ No newline at end of file
diff --git a/springboot-jsr303/src/main/java/com/itwolfed/valid/Groups.java b/springboot-jsr303/src/main/java/com/itwolfed/valid/Groups.java
new file mode 100644
index 0000000..621049d
--- /dev/null
+++ b/springboot-jsr303/src/main/java/com/itwolfed/valid/Groups.java
@@ -0,0 +1,7 @@
+package com.itwolfed.valid;
+
+
+public class Groups {
+ public interface Add{}
+ public interface Update{}
+}
diff --git a/springboot-jsr303/src/main/java/com/itwolfed/valid/ListValue.java b/springboot-jsr303/src/main/java/com/itwolfed/valid/ListValue.java
new file mode 100644
index 0000000..f3effce
--- /dev/null
+++ b/springboot-jsr303/src/main/java/com/itwolfed/valid/ListValue.java
@@ -0,0 +1,25 @@
+package com.itwolfed.valid;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.*;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+
+@Documented
+@Constraint(validatedBy = { ListValueConstraintValidator.class })
+@Target({ METHOD, FIELD, ANNOTATION_TYPE })
+@Retention(RUNTIME)
+public @interface ListValue {
+ String message() default "";
+
+ Class>[] groups() default { };
+
+ Class extends Payload>[] payload() default { };
+
+ int[] vals() ;
+}
\ No newline at end of file
diff --git a/springboot-jsr303/src/main/java/com/itwolfed/valid/ListValueConstraintValidator.java b/springboot-jsr303/src/main/java/com/itwolfed/valid/ListValueConstraintValidator.java
new file mode 100644
index 0000000..1b99a94
--- /dev/null
+++ b/springboot-jsr303/src/main/java/com/itwolfed/valid/ListValueConstraintValidator.java
@@ -0,0 +1,37 @@
+package com.itwolfed.valid;
+
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import java.util.HashSet;
+import java.util.Set;
+
+public class ListValueConstraintValidator implements ConstraintValidator {
+
+ private Set set = new HashSet<>();
+ /**
+ * 初始化方法
+ */
+ @Override
+ public void initialize(ListValue constraintAnnotation) {
+
+ int[] vals = constraintAnnotation.vals();
+ for (int val : vals) {
+ set.add(val);
+ }
+
+ }
+
+ /**
+ * 判断是否校验成功
+ *
+ * @param value 需要校验的值
+ * @param context
+ * @return
+ */
+ @Override
+ public boolean isValid(Integer value, ConstraintValidatorContext context) {
+
+ return set.contains(value);
+ }
+}
diff --git a/springboot-jsr303/src/main/resources/application.yml b/springboot-jsr303/src/main/resources/application.yml
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/springboot-jsr303/src/main/resources/application.yml
@@ -0,0 +1 @@
+
diff --git a/springboot-jsr303/src/test/java/com/itwolfed/SpringbootJsr303ApplicationTests.java b/springboot-jsr303/src/test/java/com/itwolfed/SpringbootJsr303ApplicationTests.java
new file mode 100644
index 0000000..c6cfbc9
--- /dev/null
+++ b/springboot-jsr303/src/test/java/com/itwolfed/SpringbootJsr303ApplicationTests.java
@@ -0,0 +1,13 @@
+package com.itwolfed;
+
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.SpringBootTest;
+
+@SpringBootTest
+class SpringbootJsr303ApplicationTests {
+
+ @Test
+ void contextLoads() {
+ }
+
+}
diff --git a/springboot-rocketmq-message/.gitignore b/springboot-rocketmq-message/.gitignore
new file mode 100644
index 0000000..1e4385b
--- /dev/null
+++ b/springboot-rocketmq-message/.gitignore
@@ -0,0 +1,36 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+mvnw.cmd
+mvnw
+.mvn
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
diff --git a/springboot-rocketmq-message/pom.xml b/springboot-rocketmq-message/pom.xml
new file mode 100644
index 0000000..310f5b8
--- /dev/null
+++ b/springboot-rocketmq-message/pom.xml
@@ -0,0 +1,57 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.10.RELEASE
+
+
+ com.itwolfed
+ springboot-rocketmq-message
+ 0.0.1-SNAPSHOT
+ springboot-rocketmq-message
+ Demo project for Spring Boot
+
+ 1.8
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.apache.rocketmq
+ rocketmq-spring-boot-starter
+ 2.1.1
+
+
+ org.projectlombok
+ lombok
+ 1.18.16
+ provided
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/springboot-rocketmq-message/src/main/java/com/itwolfed/RocketmqMessageNormalApplication.java b/springboot-rocketmq-message/src/main/java/com/itwolfed/RocketmqMessageNormalApplication.java
new file mode 100644
index 0000000..43e68eb
--- /dev/null
+++ b/springboot-rocketmq-message/src/main/java/com/itwolfed/RocketmqMessageNormalApplication.java
@@ -0,0 +1,13 @@
+package com.itwolfed;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class RocketmqMessageNormalApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run( RocketmqMessageNormalApplication.class, args );
+ }
+
+}
diff --git a/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/Consumer.java b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/Consumer.java
new file mode 100644
index 0000000..090ac08
--- /dev/null
+++ b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/Consumer.java
@@ -0,0 +1,18 @@
+package com.itwolfed.msg;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
+import org.apache.rocketmq.spring.core.RocketMQListener;
+import org.springframework.stereotype.Component;
+
+@Component
+@Slf4j
+@RocketMQMessageListener(topic = "Topic-Normal",consumerGroup = "consumer-msg-group")
+public class Consumer implements RocketMQListener {
+
+ @Override
+ public void onMessage(String message) {
+ log.info("Receive message:{}" , message);
+ }
+
+}
diff --git a/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/DelayConsumer.java b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/DelayConsumer.java
new file mode 100644
index 0000000..f85a4c7
--- /dev/null
+++ b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/DelayConsumer.java
@@ -0,0 +1,18 @@
+package com.itwolfed.msg;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
+import org.apache.rocketmq.spring.core.RocketMQListener;
+import org.springframework.stereotype.Component;
+
+@Component
+@Slf4j
+@RocketMQMessageListener(topic = "Topic-Delay",consumerGroup = "consumer-delay-group")
+public class DelayConsumer implements RocketMQListener {
+
+ @Override
+ public void onMessage(String message) {
+ log.info("Receive message:{}" , message);
+ }
+
+}
diff --git a/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/OrderConsumer.java b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/OrderConsumer.java
new file mode 100644
index 0000000..994aa25
--- /dev/null
+++ b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/OrderConsumer.java
@@ -0,0 +1,18 @@
+package com.itwolfed.msg;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
+import org.apache.rocketmq.spring.core.RocketMQListener;
+import org.springframework.stereotype.Component;
+
+@Component
+@Slf4j
+@RocketMQMessageListener(topic = "Topic-Order",consumerGroup = "consumer-order-group")
+public class OrderConsumer implements RocketMQListener {
+
+ @Override
+ public void onMessage(String message) {
+ log.info("Receive message:{}" , message);
+ }
+
+}
diff --git a/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/Producer.java b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/Producer.java
new file mode 100644
index 0000000..71b9709
--- /dev/null
+++ b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/Producer.java
@@ -0,0 +1,128 @@
+package com.itwolfed.msg;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.rocketmq.client.producer.LocalTransactionState;
+import org.apache.rocketmq.client.producer.SendCallback;
+import org.apache.rocketmq.client.producer.SendResult;
+import org.apache.rocketmq.client.producer.SendStatus;
+import org.apache.rocketmq.client.producer.TransactionSendResult;
+import org.apache.rocketmq.common.message.Message;
+import org.apache.rocketmq.spring.core.RocketMQTemplate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.messaging.support.MessageBuilder;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Component
+public class Producer {
+
+
+ @Autowired
+ private RocketMQTemplate rocketMQTemplate;
+
+ /**
+ * 普通消息
+ */
+ public void sendMsg() throws Exception {
+
+ Message message = new Message(
+ // 普通消息所属的Topic
+ "Topic-Normal",
+ // Message Tag可理解为Gmail中的标签,对消息进行再归类,方便Consumer指定过滤条件在消息队列 RocketMQ 版的服务器过滤。
+ "TagA",
+ // Message Body可以是任何二进制形式的数据。
+ "Hello MQ".getBytes()
+ );
+ rocketMQTemplate.getProducer().send( message );
+ // 等同于上面的方式(常用)
+ //rocketMQTemplate.convertAndSend("Topic-Normal:TagA","Hello MQ".getBytes());
+ }
+
+ /**
+ * 异步发送普通消息
+ */
+ public void sendAsyncMsg() {
+ Map map = new HashMap<>();
+ map.put( "name", "zs" );
+ map.put( "age", 20 );
+
+ rocketMQTemplate.asyncSend( "Topic-Normal", map, new SendCallback() {
+ @Override
+ public void onSuccess(SendResult sendResult) {
+ // 消息发送成功。
+ log.info( "async send success" );
+ }
+
+ @Override
+ public void onException(Throwable throwable) {
+ // 消息发送失败,需要进行重试处理,可重新发送这条消息或持久化这条数据进行补偿处理。
+ log.info( "async send fail" );
+ }
+ } );
+ }
+
+ /**
+ * 顺序消息
+ */
+ public void sendOrderlyMsg() {
+ //根据指定的hashKey按顺序发送
+ for (int i = 0; i < 1000; i++) {
+ String orderId = "biz_" + i % 10;
+ // 分区顺序消息中区分不同分区的关键字段,Sharding Key与普通消息的key是完全不同的概念。
+ // 全局顺序消息,该字段可以设置为任意非空字符串。
+ String shardingKey = String.valueOf( orderId );
+ try {
+ SendResult sendResult = rocketMQTemplate.syncSendOrderly( "Topic-Order", "send order msg".getBytes(), shardingKey );
+ // 发送消息,只要不抛异常就是成功。
+ if (sendResult != null) {
+ System.out.println( new Date() + " Send mq message success . msgId is:" + sendResult.getMsgId() );
+ }
+ } catch (Exception e) {
+ // 消息发送失败,需要进行重试处理,可重新发送这条消息或持久化这条数据进行补偿处理。
+ System.out.println( new Date() + " Send mq message failed" );
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * 延时消息
+ */
+ public void sendDelayMsg() {
+ rocketMQTemplate.syncSend( "Topic-Delay",
+ MessageBuilder.withPayload( "Hello MQ".getBytes() ).build(),
+ 3000,
+ //设置延时等级3,这个消息将在10s之后发送(现在只支持固定的几个时间,详看delayTimeLevel)
+ //messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h";
+ 3 );
+ }
+
+ /**
+ * 批量消息
+ */
+ public void sendBatchMsg(List messages) {
+ rocketMQTemplate.syncSend( "springboot-rocketmq", messages );
+
+ }
+
+ /**
+ * 事务消息
+ */
+ public void sendTransactionMsg(){
+ TransactionSendResult transactionSendResult = rocketMQTemplate.sendMessageInTransaction(
+ "Topic-Tx:TagA",
+ MessageBuilder.withPayload( "Hello MQ transaction===".getBytes() ).build(),
+ null );
+ SendStatus sendStatus = transactionSendResult.getSendStatus();
+ LocalTransactionState localTransactionState = transactionSendResult.getLocalTransactionState();
+
+ System.out.println( new Date() + " Send mq message status "+ sendStatus +" , localTransactionState "+ localTransactionState );
+ }
+
+
+}
diff --git a/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/TxConsumerListener.java b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/TxConsumerListener.java
new file mode 100644
index 0000000..84975de
--- /dev/null
+++ b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/TxConsumerListener.java
@@ -0,0 +1,22 @@
+package com.itwolfed.msg;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
+import org.apache.rocketmq.spring.core.RocketMQListener;
+import org.springframework.stereotype.Component;
+
+
+@Component
+@Slf4j
+@RocketMQMessageListener(topic = "Topic-Tx",consumerGroup = "consumer-tx-group")
+public class TxConsumerListener implements RocketMQListener {
+
+
+
+ @Override
+ public void onMessage(String message) {
+
+ log.info("Receive message:{}" , message);
+ }
+
+}
diff --git a/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/TxProducerListener.java b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/TxProducerListener.java
new file mode 100644
index 0000000..3eb5c8a
--- /dev/null
+++ b/springboot-rocketmq-message/src/main/java/com/itwolfed/msg/TxProducerListener.java
@@ -0,0 +1,45 @@
+package com.itwolfed.msg;
+
+import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
+import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
+import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
+import org.springframework.messaging.Message;
+import org.springframework.stereotype.Component;
+
+
+@Component
+@RocketMQTransactionListener
+public class TxProducerListener implements RocketMQLocalTransactionListener {
+
+ @Override
+ public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
+ // 执行本地事务
+ System.out.println("TX message listener execute local transaction");
+ RocketMQLocalTransactionState result;
+ try {
+ // 业务代码( 例如下订单 )
+ result = RocketMQLocalTransactionState.COMMIT;
+ } catch (Exception e) {
+ System.out.println("execute local transaction error");
+ result = RocketMQLocalTransactionState.UNKNOWN;
+ }
+ return result;
+ }
+
+ @Override
+ public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
+ // 检查本地事务( 例如检查下订单是否成功 )
+ System.out.println("TX message listener check local transaction");
+ RocketMQLocalTransactionState result;
+ try {
+ //业务代码( 根据检查结果,决定是COMMIT或ROLLBACK )
+ result = RocketMQLocalTransactionState.COMMIT;
+ } catch (Exception e) {
+ // 异常就回滚
+ System.out.println("check local transaction error");
+ result = RocketMQLocalTransactionState.ROLLBACK;
+ }
+ return result;
+ }
+
+}
diff --git a/springboot-rocketmq-message/src/main/resources/application.yml b/springboot-rocketmq-message/src/main/resources/application.yml
new file mode 100644
index 0000000..fa3511d
--- /dev/null
+++ b/springboot-rocketmq-message/src/main/resources/application.yml
@@ -0,0 +1,7 @@
+server:
+ port: 8080
+
+rocketmq:
+ name-server: 172.16.250.129:9876
+ producer:
+ group: myGroup
\ No newline at end of file
diff --git a/springboot-rocketmq-message/src/test/java/com/itwolfed/RocketmqMessageNormalApplicationTests.java b/springboot-rocketmq-message/src/test/java/com/itwolfed/RocketmqMessageNormalApplicationTests.java
new file mode 100644
index 0000000..29f75c4
--- /dev/null
+++ b/springboot-rocketmq-message/src/test/java/com/itwolfed/RocketmqMessageNormalApplicationTests.java
@@ -0,0 +1,62 @@
+package com.itwolfed;
+
+
+import com.itwolfed.msg.Producer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class RocketmqMessageNormalApplicationTests {
+
+ @Autowired
+ private Producer producer;
+
+
+
+ /**
+ * 测试普通消息
+ */
+ @Test
+ public void sendMsg() throws Exception {
+ producer.sendMsg();
+ }
+
+ /**
+ * 异步发送普通消息
+ */
+ @Test
+ public void sendAsyncMsg() {
+ producer.sendAsyncMsg();
+ }
+
+ /**
+ * 顺序消息
+ */
+ @Test
+ public void sendOrderlyMsg() {
+ producer.sendOrderlyMsg();
+ }
+
+ /**
+ * 延时消息
+ */
+ @Test
+ public void sendDelayMsg() {
+ producer.sendDelayMsg();
+ }
+
+ /**
+ * 事务消息
+ */
+ @Test
+ public void sendTransactionMsg(){
+ producer.sendTransactionMsg();
+ }
+
+
+}
diff --git a/springboot-seata/sbm-account-service/.gitignore b/springboot-seata/sbm-account-service/.gitignore
new file mode 100755
index 0000000..a2a3040
--- /dev/null
+++ b/springboot-seata/sbm-account-service/.gitignore
@@ -0,0 +1,31 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/
diff --git a/springboot-seata/sbm-account-service/.mvn/wrapper/MavenWrapperDownloader.java b/springboot-seata/sbm-account-service/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100755
index 0000000..7f91a56
--- /dev/null
+++ b/springboot-seata/sbm-account-service/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,114 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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.
+*/
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL =
+ "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if (mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if (mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: : " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if (!outputFile.getParentFile().exists()) {
+ if (!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/springboot-seata/sbm-account-service/.mvn/wrapper/maven-wrapper.jar b/springboot-seata/sbm-account-service/.mvn/wrapper/maven-wrapper.jar
new file mode 100755
index 0000000..01e6799
Binary files /dev/null and b/springboot-seata/sbm-account-service/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/springboot-seata/sbm-account-service/.mvn/wrapper/maven-wrapper.properties b/springboot-seata/sbm-account-service/.mvn/wrapper/maven-wrapper.properties
new file mode 100755
index 0000000..cd0d451
--- /dev/null
+++ b/springboot-seata/sbm-account-service/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip
diff --git a/springboot-seata/sbm-account-service/pom.xml b/springboot-seata/sbm-account-service/pom.xml
new file mode 100755
index 0000000..af89d5d
--- /dev/null
+++ b/springboot-seata/sbm-account-service/pom.xml
@@ -0,0 +1,56 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.10.RELEASE
+
+
+ com.gf
+ sbm-account-service
+ 0.0.1-SNAPSHOT
+ sbm-account-service
+ Demo project for Spring Boot
+
+
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ io.seata
+ seata-spring-boot-starter
+ 1.0.0
+
+
+ org.projectlombok
+ lombok
+
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ 2.1.0
+
+
+ mysql
+ mysql-connector-java
+ runtime
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/springboot-seata/sbm-account-service/src/main/java/com/gf/SbmAccountServiceApplication.java b/springboot-seata/sbm-account-service/src/main/java/com/gf/SbmAccountServiceApplication.java
new file mode 100755
index 0000000..a209f9f
--- /dev/null
+++ b/springboot-seata/sbm-account-service/src/main/java/com/gf/SbmAccountServiceApplication.java
@@ -0,0 +1,22 @@
+package com.gf;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.client.RestTemplate;
+
+@SpringBootApplication(scanBasePackages = "com.gf", exclude = DataSourceAutoConfiguration.class)
+public class SbmAccountServiceApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SbmAccountServiceApplication.class, args);
+ }
+
+ @Bean
+ public RestTemplate restTemplate() {
+ RestTemplate restTemplate = new RestTemplate();
+ return restTemplate;
+ }
+
+}
diff --git a/springboot-seata/sbm-account-service/src/main/java/com/gf/config/DataSourceConfig.java b/springboot-seata/sbm-account-service/src/main/java/com/gf/config/DataSourceConfig.java
new file mode 100755
index 0000000..0d0a6a8
--- /dev/null
+++ b/springboot-seata/sbm-account-service/src/main/java/com/gf/config/DataSourceConfig.java
@@ -0,0 +1,21 @@
+package com.gf.config;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import javax.sql.DataSource;
+
+@Configuration
+public class DataSourceConfig {
+
+ @Bean
+ @ConfigurationProperties(prefix = "spring.datasource")
+ public DataSource druidDataSource() {
+ DruidDataSource druidDataSource = new DruidDataSource();
+ return druidDataSource;
+ }
+
+
+}
\ No newline at end of file
diff --git a/springboot-seata/sbm-account-service/src/main/java/com/gf/config/SeataRestTemplateAutoConfiguration.java b/springboot-seata/sbm-account-service/src/main/java/com/gf/config/SeataRestTemplateAutoConfiguration.java
new file mode 100755
index 0000000..b8c3778
--- /dev/null
+++ b/springboot-seata/sbm-account-service/src/main/java/com/gf/config/SeataRestTemplateAutoConfiguration.java
@@ -0,0 +1,47 @@
+package com.gf.config;
+
+import com.gf.interceptor.SeataRestTemplateInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.PostConstruct;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+@Configuration
+public class SeataRestTemplateAutoConfiguration {
+ @Autowired(
+ required = false
+ )
+ private Collection restTemplates;
+ @Autowired
+ private SeataRestTemplateInterceptor seataRestTemplateInterceptor;
+
+ public SeataRestTemplateAutoConfiguration() {
+ }
+
+ @Bean
+ public SeataRestTemplateInterceptor seataRestTemplateInterceptor() {
+ return new SeataRestTemplateInterceptor();
+ }
+
+ @PostConstruct
+ public void init() {
+ if (this.restTemplates != null) {
+ Iterator var1 = this.restTemplates.iterator();
+
+ while (var1.hasNext()) {
+ RestTemplate restTemplate = (RestTemplate) var1.next();
+ List interceptors = new ArrayList(restTemplate.getInterceptors());
+ interceptors.add(this.seataRestTemplateInterceptor);
+ restTemplate.setInterceptors(interceptors);
+ }
+ }
+
+ }
+}
diff --git a/springboot-seata/sbm-account-service/src/main/java/com/gf/controller/AccountController.java b/springboot-seata/sbm-account-service/src/main/java/com/gf/controller/AccountController.java
new file mode 100755
index 0000000..c2a588f
--- /dev/null
+++ b/springboot-seata/sbm-account-service/src/main/java/com/gf/controller/AccountController.java
@@ -0,0 +1,25 @@
+package com.gf.controller;
+
+import com.gf.service.AccountService;
+import io.seata.core.context.RootContext;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.math.BigDecimal;
+
+@RestController
+@RequiredArgsConstructor(onConstructor_={@Autowired})
+public class AccountController {
+
+ private final AccountService accountService;
+
+ @GetMapping
+ public void debit(@RequestParam String userId , @RequestParam BigDecimal orderMoney) {
+ System.out.println("account XID " + RootContext.getXID());
+ accountService.debit(userId , orderMoney);
+ }
+
+}
diff --git a/springboot-seata/sbm-account-service/src/main/java/com/gf/entity/Account.java b/springboot-seata/sbm-account-service/src/main/java/com/gf/entity/Account.java
new file mode 100755
index 0000000..7cf8431
--- /dev/null
+++ b/springboot-seata/sbm-account-service/src/main/java/com/gf/entity/Account.java
@@ -0,0 +1,16 @@
+package com.gf.entity;
+
+import lombok.*;
+
+import java.math.BigDecimal;
+
+@Getter
+@Setter
+@ToString
+@AllArgsConstructor
+@NoArgsConstructor
+public class Account {
+ private Integer id;
+ private String userId;
+ private BigDecimal money;
+}
diff --git a/springboot-seata/sbm-account-service/src/main/java/com/gf/filter/SeataFilter.java b/springboot-seata/sbm-account-service/src/main/java/com/gf/filter/SeataFilter.java
new file mode 100755
index 0000000..cf6e77f
--- /dev/null
+++ b/springboot-seata/sbm-account-service/src/main/java/com/gf/filter/SeataFilter.java
@@ -0,0 +1,43 @@
+package com.gf.filter;
+
+import io.seata.core.context.RootContext;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+@Component
+public class SeataFilter implements Filter {
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest req = (HttpServletRequest) servletRequest;
+ String xid = req.getHeader(RootContext.KEY_XID.toLowerCase());
+ boolean isBind = false;
+ if (StringUtils.isNotBlank(xid)) {
+ RootContext.bind(xid);
+ isBind = true;
+ }
+ try {
+ filterChain.doFilter(servletRequest, servletResponse);
+ } finally {
+ if (isBind) {
+ RootContext.unbind();
+ }
+ }
+ }
+
+ @Override
+ public void destroy() {
+ }
+}
diff --git a/springboot-seata/sbm-account-service/src/main/java/com/gf/interceptor/SeataRestTemplateInterceptor.java b/springboot-seata/sbm-account-service/src/main/java/com/gf/interceptor/SeataRestTemplateInterceptor.java
new file mode 100755
index 0000000..ac547a0
--- /dev/null
+++ b/springboot-seata/sbm-account-service/src/main/java/com/gf/interceptor/SeataRestTemplateInterceptor.java
@@ -0,0 +1,27 @@
+package com.gf.interceptor;
+
+import io.seata.core.context.RootContext;
+import org.apache.commons.lang.StringUtils;
+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.http.client.support.HttpRequestWrapper;
+
+import java.io.IOException;
+
+public class SeataRestTemplateInterceptor implements ClientHttpRequestInterceptor {
+ public SeataRestTemplateInterceptor() {
+ }
+
+ @Override
+ public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
+ HttpRequestWrapper requestWrapper = new HttpRequestWrapper(httpRequest);
+ String xid = RootContext.getXID();
+ if (StringUtils.isNotEmpty(xid)) {
+ requestWrapper.getHeaders().add(RootContext.KEY_XID, xid);
+ }
+
+ return clientHttpRequestExecution.execute(requestWrapper, bytes);
+ }
+}
diff --git a/springboot-seata/sbm-account-service/src/main/java/com/gf/mapper/AccountMapper.java b/springboot-seata/sbm-account-service/src/main/java/com/gf/mapper/AccountMapper.java
new file mode 100755
index 0000000..7210930
--- /dev/null
+++ b/springboot-seata/sbm-account-service/src/main/java/com/gf/mapper/AccountMapper.java
@@ -0,0 +1,15 @@
+package com.gf.mapper;
+
+
+import com.gf.entity.Account;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+@Mapper
+public interface AccountMapper {
+
+ Account selectByUserId(@Param("userId") String userId);
+
+ int updateById(@Param("account") Account record);
+
+}
diff --git a/springboot-seata/sbm-account-service/src/main/java/com/gf/service/AccountService.java b/springboot-seata/sbm-account-service/src/main/java/com/gf/service/AccountService.java
new file mode 100755
index 0000000..e7d8b7e
--- /dev/null
+++ b/springboot-seata/sbm-account-service/src/main/java/com/gf/service/AccountService.java
@@ -0,0 +1,29 @@
+package com.gf.service;
+
+import com.gf.entity.Account;
+import com.gf.mapper.AccountMapper;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+
+@Service
+@RequiredArgsConstructor(onConstructor_={@Autowired})
+public class AccountService {
+
+ private static final String ERROR_USER_ID = "1002";
+
+ private final AccountMapper accountMapper;
+
+ public void debit(String userId , BigDecimal num) {
+ Account account = accountMapper.selectByUserId(userId);
+ account.setMoney(account.getMoney().subtract(num));
+ accountMapper.updateById(account);
+
+ if (ERROR_USER_ID.equals(userId)) {
+ throw new RuntimeException("account branch exception");
+ }
+ }
+
+}
diff --git a/springboot-seata/sbm-account-service/src/main/resources/application.properties b/springboot-seata/sbm-account-service/src/main/resources/application.properties
new file mode 100755
index 0000000..231768c
--- /dev/null
+++ b/springboot-seata/sbm-account-service/src/main/resources/application.properties
@@ -0,0 +1,10 @@
+spring.application.name=account-service
+server.port=8081
+spring.datasource.url=jdbc:mysql://172.16.2.101:3306/db_seata?useSSL=false&serverTimezone=UTC
+spring.datasource.username=root
+spring.datasource.password=123456
+seata.tx-service-group=my_test_tx_group
+mybatis.mapper-locations=classpath*:mapper/*Mapper.xml
+seata.service.grouplist=172.16.2.101:8091
+logging.level.io.seata=info
+logging.level.io.seata.samples.account.persistence.AccountMapper=debug
diff --git a/springboot-seata/sbm-account-service/src/main/resources/mapper/AccountMapper.xml b/springboot-seata/sbm-account-service/src/main/resources/mapper/AccountMapper.xml
new file mode 100755
index 0000000..e2af415
--- /dev/null
+++ b/springboot-seata/sbm-account-service/src/main/resources/mapper/AccountMapper.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ update account_tbl
+ set money = #{account.money,jdbcType=DECIMAL}
+ where id = #{account.id}
+
+
+
\ No newline at end of file
diff --git a/springboot-seata/sbm-account-service/src/test/java/com/gf/SbmAccountServiceApplicationTests.java b/springboot-seata/sbm-account-service/src/test/java/com/gf/SbmAccountServiceApplicationTests.java
new file mode 100755
index 0000000..e69de29
diff --git a/springboot-seata/sbm-business-service/.gitignore b/springboot-seata/sbm-business-service/.gitignore
new file mode 100755
index 0000000..a2a3040
--- /dev/null
+++ b/springboot-seata/sbm-business-service/.gitignore
@@ -0,0 +1,31 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/
diff --git a/springboot-seata/sbm-business-service/.mvn/wrapper/MavenWrapperDownloader.java b/springboot-seata/sbm-business-service/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100755
index 0000000..7f91a56
--- /dev/null
+++ b/springboot-seata/sbm-business-service/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,114 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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.
+*/
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL =
+ "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if (mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if (mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: : " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if (!outputFile.getParentFile().exists()) {
+ if (!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/springboot-seata/sbm-business-service/.mvn/wrapper/maven-wrapper.jar b/springboot-seata/sbm-business-service/.mvn/wrapper/maven-wrapper.jar
new file mode 100755
index 0000000..01e6799
Binary files /dev/null and b/springboot-seata/sbm-business-service/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/springboot-seata/sbm-business-service/.mvn/wrapper/maven-wrapper.properties b/springboot-seata/sbm-business-service/.mvn/wrapper/maven-wrapper.properties
new file mode 100755
index 0000000..cd0d451
--- /dev/null
+++ b/springboot-seata/sbm-business-service/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip
diff --git a/springboot-seata/sbm-business-service/pom.xml b/springboot-seata/sbm-business-service/pom.xml
new file mode 100755
index 0000000..dba052e
--- /dev/null
+++ b/springboot-seata/sbm-business-service/pom.xml
@@ -0,0 +1,46 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.10.RELEASE
+
+
+ com.gf
+ sbm-business-service
+ 0.0.1-SNAPSHOT
+ sbm-business-service
+ Demo project for Spring Boot
+
+
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.projectlombok
+ lombok
+
+
+ io.seata
+ seata-spring-boot-starter
+ 1.0.0
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/springboot-seata/sbm-business-service/src/main/java/com/gf/SbmBusinessServiceApplication.java b/springboot-seata/sbm-business-service/src/main/java/com/gf/SbmBusinessServiceApplication.java
new file mode 100755
index 0000000..e5975bf
--- /dev/null
+++ b/springboot-seata/sbm-business-service/src/main/java/com/gf/SbmBusinessServiceApplication.java
@@ -0,0 +1,23 @@
+package com.gf;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.client.RestTemplate;
+
+@SpringBootApplication(scanBasePackages = "com.gf", exclude = DataSourceAutoConfiguration.class)
+public class SbmBusinessServiceApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SbmBusinessServiceApplication.class, args);
+ }
+
+ @Bean
+ public RestTemplate restTemplate() {
+ RestTemplate restTemplate = new RestTemplate();
+ return restTemplate;
+ }
+
+
+}
diff --git a/springboot-seata/sbm-business-service/src/main/java/com/gf/client/OrderClient.java b/springboot-seata/sbm-business-service/src/main/java/com/gf/client/OrderClient.java
new file mode 100755
index 0000000..bc1ba65
--- /dev/null
+++ b/springboot-seata/sbm-business-service/src/main/java/com/gf/client/OrderClient.java
@@ -0,0 +1,25 @@
+package com.gf.client;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor(onConstructor_={@Autowired})
+public class OrderClient {
+
+ private final RestTemplate restTemplate;
+
+ public void create(String userId, String commodityCode, int orderCount) {
+ String url = "http://127.0.0.1:8082/api/order/debit?userId=" + userId + "&commodityCode=" + commodityCode + "&count=" + orderCount;
+ try {
+ restTemplate.getForEntity(url, Void.class);
+ } catch (Exception e) {
+ log.error("create url {} ,error:", url);
+ throw new RuntimeException();
+ }
+ }
+}
diff --git a/springboot-seata/sbm-business-service/src/main/java/com/gf/client/StorageClient.java b/springboot-seata/sbm-business-service/src/main/java/com/gf/client/StorageClient.java
new file mode 100755
index 0000000..7fc9eed
--- /dev/null
+++ b/springboot-seata/sbm-business-service/src/main/java/com/gf/client/StorageClient.java
@@ -0,0 +1,27 @@
+package com.gf.client;
+
+import io.seata.core.context.RootContext;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor(onConstructor_={@Autowired})
+public class StorageClient {
+
+ private final RestTemplate restTemplate;
+
+ public void deduct(String commodityCode, int orderCount) {
+ System.out.println("business to storage " + RootContext.getXID());
+ String url = "http://127.0.0.1:8083/api/storage/deduct?commodityCode=" + commodityCode + "&count=" + orderCount;
+ try {
+ restTemplate.getForEntity(url, Void.class);
+ } catch (Exception e) {
+ log.error("deduct url {} ,error:", url, e);
+ throw new RuntimeException();
+ }
+ }
+}
diff --git a/springboot-seata/sbm-business-service/src/main/java/com/gf/config/SeataRestTemplateAutoConfiguration.java b/springboot-seata/sbm-business-service/src/main/java/com/gf/config/SeataRestTemplateAutoConfiguration.java
new file mode 100755
index 0000000..b8c3778
--- /dev/null
+++ b/springboot-seata/sbm-business-service/src/main/java/com/gf/config/SeataRestTemplateAutoConfiguration.java
@@ -0,0 +1,47 @@
+package com.gf.config;
+
+import com.gf.interceptor.SeataRestTemplateInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.PostConstruct;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+@Configuration
+public class SeataRestTemplateAutoConfiguration {
+ @Autowired(
+ required = false
+ )
+ private Collection restTemplates;
+ @Autowired
+ private SeataRestTemplateInterceptor seataRestTemplateInterceptor;
+
+ public SeataRestTemplateAutoConfiguration() {
+ }
+
+ @Bean
+ public SeataRestTemplateInterceptor seataRestTemplateInterceptor() {
+ return new SeataRestTemplateInterceptor();
+ }
+
+ @PostConstruct
+ public void init() {
+ if (this.restTemplates != null) {
+ Iterator var1 = this.restTemplates.iterator();
+
+ while (var1.hasNext()) {
+ RestTemplate restTemplate = (RestTemplate) var1.next();
+ List interceptors = new ArrayList(restTemplate.getInterceptors());
+ interceptors.add(this.seataRestTemplateInterceptor);
+ restTemplate.setInterceptors(interceptors);
+ }
+ }
+
+ }
+}
diff --git a/springboot-seata/sbm-business-service/src/main/java/com/gf/controller/BusinessController.java b/springboot-seata/sbm-business-service/src/main/java/com/gf/controller/BusinessController.java
new file mode 100755
index 0000000..adbd415
--- /dev/null
+++ b/springboot-seata/sbm-business-service/src/main/java/com/gf/controller/BusinessController.java
@@ -0,0 +1,45 @@
+package com.gf.controller;
+
+import com.gf.service.BusinessService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+
+@RequestMapping("/api/business")
+@RestController
+@RequiredArgsConstructor(onConstructor_={@Autowired})
+public class BusinessController {
+
+ private final BusinessService businessService;
+
+ /**
+ * 购买下单,模拟全局事务提交
+ *
+ * @return
+ */
+ @RequestMapping("/purchase/commit")
+ public Boolean purchaseCommit(HttpServletRequest request) {
+ businessService.purchase("1001", "2001", 1);
+ return true;
+ }
+
+ /**
+ * 购买下单,模拟全局事务回滚
+ *
+ * @return
+ */
+ @RequestMapping("/purchase/rollback")
+ public Boolean purchaseRollback() {
+ try {
+ businessService.purchase("1002", "2001", 1);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/springboot-seata/sbm-business-service/src/main/java/com/gf/filter/SeataFilter.java b/springboot-seata/sbm-business-service/src/main/java/com/gf/filter/SeataFilter.java
new file mode 100755
index 0000000..cf6e77f
--- /dev/null
+++ b/springboot-seata/sbm-business-service/src/main/java/com/gf/filter/SeataFilter.java
@@ -0,0 +1,43 @@
+package com.gf.filter;
+
+import io.seata.core.context.RootContext;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+@Component
+public class SeataFilter implements Filter {
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest req = (HttpServletRequest) servletRequest;
+ String xid = req.getHeader(RootContext.KEY_XID.toLowerCase());
+ boolean isBind = false;
+ if (StringUtils.isNotBlank(xid)) {
+ RootContext.bind(xid);
+ isBind = true;
+ }
+ try {
+ filterChain.doFilter(servletRequest, servletResponse);
+ } finally {
+ if (isBind) {
+ RootContext.unbind();
+ }
+ }
+ }
+
+ @Override
+ public void destroy() {
+ }
+}
diff --git a/springboot-seata/sbm-business-service/src/main/java/com/gf/interceptor/SeataRestTemplateInterceptor.java b/springboot-seata/sbm-business-service/src/main/java/com/gf/interceptor/SeataRestTemplateInterceptor.java
new file mode 100755
index 0000000..ac547a0
--- /dev/null
+++ b/springboot-seata/sbm-business-service/src/main/java/com/gf/interceptor/SeataRestTemplateInterceptor.java
@@ -0,0 +1,27 @@
+package com.gf.interceptor;
+
+import io.seata.core.context.RootContext;
+import org.apache.commons.lang.StringUtils;
+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.http.client.support.HttpRequestWrapper;
+
+import java.io.IOException;
+
+public class SeataRestTemplateInterceptor implements ClientHttpRequestInterceptor {
+ public SeataRestTemplateInterceptor() {
+ }
+
+ @Override
+ public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
+ HttpRequestWrapper requestWrapper = new HttpRequestWrapper(httpRequest);
+ String xid = RootContext.getXID();
+ if (StringUtils.isNotEmpty(xid)) {
+ requestWrapper.getHeaders().add(RootContext.KEY_XID, xid);
+ }
+
+ return clientHttpRequestExecution.execute(requestWrapper, bytes);
+ }
+}
diff --git a/springboot-seata/sbm-business-service/src/main/java/com/gf/service/BusinessService.java b/springboot-seata/sbm-business-service/src/main/java/com/gf/service/BusinessService.java
new file mode 100755
index 0000000..f2b3c93
--- /dev/null
+++ b/springboot-seata/sbm-business-service/src/main/java/com/gf/service/BusinessService.java
@@ -0,0 +1,36 @@
+package com.gf.service;
+
+import com.gf.client.OrderClient;
+import com.gf.client.StorageClient;
+import io.seata.core.context.RootContext;
+import io.seata.spring.annotation.GlobalTransactional;
+import lombok.RequiredArgsConstructor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor(onConstructor_={@Autowired})
+public class BusinessService {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BusinessService.class);
+
+ private final StorageClient storageClient;
+
+ private final OrderClient orderClient;
+
+ /**
+ * 减库存,下订单
+ *
+ * @param userId
+ * @param commodityCode
+ * @param orderCount
+ */
+ @GlobalTransactional
+ public void purchase(String userId, String commodityCode, int orderCount) {
+ LOGGER.info("purchase begin ... xid: " + RootContext.getXID());
+ storageClient.deduct(commodityCode, orderCount);
+ orderClient.create(userId, commodityCode, orderCount);
+ }
+}
diff --git a/springboot-seata/sbm-business-service/src/main/resources/application.properties b/springboot-seata/sbm-business-service/src/main/resources/application.properties
new file mode 100755
index 0000000..8aad42c
--- /dev/null
+++ b/springboot-seata/sbm-business-service/src/main/resources/application.properties
@@ -0,0 +1,5 @@
+spring.application.name=business-service
+server.port=8084
+seata.service.grouplist=172.16.2.101:8091
+seata.tx-service-group=my_test_tx_group
+logging.level.io.seata=info
\ No newline at end of file
diff --git a/springboot-seata/sbm-business-service/src/test/java/com/gf/SbmBusinessServiceApplicationTests.java b/springboot-seata/sbm-business-service/src/test/java/com/gf/SbmBusinessServiceApplicationTests.java
new file mode 100755
index 0000000..e69de29
diff --git a/springboot-seata/sbm-order-service/.gitignore b/springboot-seata/sbm-order-service/.gitignore
new file mode 100755
index 0000000..a2a3040
--- /dev/null
+++ b/springboot-seata/sbm-order-service/.gitignore
@@ -0,0 +1,31 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/
diff --git a/springboot-seata/sbm-order-service/.mvn/wrapper/MavenWrapperDownloader.java b/springboot-seata/sbm-order-service/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100755
index 0000000..7f91a56
--- /dev/null
+++ b/springboot-seata/sbm-order-service/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,114 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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.
+*/
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL =
+ "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if (mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if (mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: : " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if (!outputFile.getParentFile().exists()) {
+ if (!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/springboot-seata/sbm-order-service/.mvn/wrapper/maven-wrapper.jar b/springboot-seata/sbm-order-service/.mvn/wrapper/maven-wrapper.jar
new file mode 100755
index 0000000..01e6799
Binary files /dev/null and b/springboot-seata/sbm-order-service/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/springboot-seata/sbm-order-service/.mvn/wrapper/maven-wrapper.properties b/springboot-seata/sbm-order-service/.mvn/wrapper/maven-wrapper.properties
new file mode 100755
index 0000000..cd0d451
--- /dev/null
+++ b/springboot-seata/sbm-order-service/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip
diff --git a/springboot-seata/sbm-order-service/pom.xml b/springboot-seata/sbm-order-service/pom.xml
new file mode 100755
index 0000000..e31e09a
--- /dev/null
+++ b/springboot-seata/sbm-order-service/pom.xml
@@ -0,0 +1,56 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.10.RELEASE
+
+
+ com.gf
+ sbm-order-service
+ 0.0.1-SNAPSHOT
+ sbm-order-service
+ Demo project for Spring Boot
+
+
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ io.seata
+ seata-spring-boot-starter
+ 1.0.0
+
+
+ org.projectlombok
+ lombok
+
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ 2.1.0
+
+
+ mysql
+ mysql-connector-java
+ runtime
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/springboot-seata/sbm-order-service/src/main/java/com/gf/SbmOrderServiceApplication.java b/springboot-seata/sbm-order-service/src/main/java/com/gf/SbmOrderServiceApplication.java
new file mode 100755
index 0000000..a447e98
--- /dev/null
+++ b/springboot-seata/sbm-order-service/src/main/java/com/gf/SbmOrderServiceApplication.java
@@ -0,0 +1,23 @@
+package com.gf;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.web.client.RestTemplate;
+
+
+@SpringBootApplication(scanBasePackages = "com.gf", exclude = DataSourceAutoConfiguration.class)
+public class SbmOrderServiceApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SbmOrderServiceApplication.class, args);
+ }
+
+ @Bean
+ public RestTemplate restTemplate() {
+ RestTemplate restTemplate = new RestTemplate();
+ return restTemplate;
+ }
+
+}
diff --git a/springboot-seata/sbm-order-service/src/main/java/com/gf/client/AccountClient.java b/springboot-seata/sbm-order-service/src/main/java/com/gf/client/AccountClient.java
new file mode 100755
index 0000000..17e8a50
--- /dev/null
+++ b/springboot-seata/sbm-order-service/src/main/java/com/gf/client/AccountClient.java
@@ -0,0 +1,28 @@
+package com.gf.client;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
+
+import java.math.BigDecimal;
+
+@Slf4j
+@Component
+@RequiredArgsConstructor(onConstructor_={@Autowired})
+public class AccountClient {
+
+ private final RestTemplate restTemplate;
+
+ public void debit(String userId, BigDecimal orderMoney) {
+ String url = "http://127.0.0.1:8081?userId=" + userId + "&orderMoney=" + orderMoney;
+ try {
+ restTemplate.getForEntity(url, Void.class);
+ } catch (Exception e) {
+ log.error("debit url {} ,error:", url, e);
+ throw new RuntimeException();
+ }
+ }
+
+}
diff --git a/springboot-seata/sbm-order-service/src/main/java/com/gf/config/DataSourceConfig.java b/springboot-seata/sbm-order-service/src/main/java/com/gf/config/DataSourceConfig.java
new file mode 100755
index 0000000..82d94b3
--- /dev/null
+++ b/springboot-seata/sbm-order-service/src/main/java/com/gf/config/DataSourceConfig.java
@@ -0,0 +1,26 @@
+package com.gf.config;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import io.seata.rm.datasource.DataSourceProxy;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.mybatis.spring.SqlSessionFactoryBean;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+
+import javax.sql.DataSource;
+
+@Configuration
+public class DataSourceConfig {
+
+ @Bean
+ @ConfigurationProperties(prefix = "spring.datasource")
+ public DataSource druidDataSource() {
+ DruidDataSource druidDataSource = new DruidDataSource();
+ return druidDataSource;
+ }
+
+
+}
\ No newline at end of file
diff --git a/springboot-seata/sbm-order-service/src/main/java/com/gf/config/SeataRestTemplateAutoConfiguration.java b/springboot-seata/sbm-order-service/src/main/java/com/gf/config/SeataRestTemplateAutoConfiguration.java
new file mode 100755
index 0000000..b8c3778
--- /dev/null
+++ b/springboot-seata/sbm-order-service/src/main/java/com/gf/config/SeataRestTemplateAutoConfiguration.java
@@ -0,0 +1,47 @@
+package com.gf.config;
+
+import com.gf.interceptor.SeataRestTemplateInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.PostConstruct;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+@Configuration
+public class SeataRestTemplateAutoConfiguration {
+ @Autowired(
+ required = false
+ )
+ private Collection restTemplates;
+ @Autowired
+ private SeataRestTemplateInterceptor seataRestTemplateInterceptor;
+
+ public SeataRestTemplateAutoConfiguration() {
+ }
+
+ @Bean
+ public SeataRestTemplateInterceptor seataRestTemplateInterceptor() {
+ return new SeataRestTemplateInterceptor();
+ }
+
+ @PostConstruct
+ public void init() {
+ if (this.restTemplates != null) {
+ Iterator var1 = this.restTemplates.iterator();
+
+ while (var1.hasNext()) {
+ RestTemplate restTemplate = (RestTemplate) var1.next();
+ List interceptors = new ArrayList(restTemplate.getInterceptors());
+ interceptors.add(this.seataRestTemplateInterceptor);
+ restTemplate.setInterceptors(interceptors);
+ }
+ }
+
+ }
+}
diff --git a/springboot-seata/sbm-order-service/src/main/java/com/gf/controller/OrderController.java b/springboot-seata/sbm-order-service/src/main/java/com/gf/controller/OrderController.java
new file mode 100755
index 0000000..260d634
--- /dev/null
+++ b/springboot-seata/sbm-order-service/src/main/java/com/gf/controller/OrderController.java
@@ -0,0 +1,25 @@
+package com.gf.controller;
+
+import com.gf.service.OrderService;
+import io.seata.core.context.RootContext;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RequestMapping("/api/order")
+@RestController
+@RequiredArgsConstructor(onConstructor_={@Autowired})
+public class OrderController {
+
+ private final OrderService orderService;
+
+ @GetMapping(value = "/debit")
+ public void debit(@RequestParam String userId, @RequestParam String commodityCode, @RequestParam Integer count) {
+ System.out.println("order XID " + RootContext.getXID());
+ orderService.create(userId, commodityCode, count);
+ }
+
+}
diff --git a/springboot-seata/sbm-order-service/src/main/java/com/gf/entity/Order.java b/springboot-seata/sbm-order-service/src/main/java/com/gf/entity/Order.java
new file mode 100755
index 0000000..b221bca
--- /dev/null
+++ b/springboot-seata/sbm-order-service/src/main/java/com/gf/entity/Order.java
@@ -0,0 +1,23 @@
+package com.gf.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.math.BigDecimal;
+
+@Getter
+@Setter
+@ToString
+public class Order {
+ private Integer id;
+
+ private String userId;
+
+ private String commodityCode;
+
+ private Integer count;
+
+ private BigDecimal money;
+
+}
diff --git a/springboot-seata/sbm-order-service/src/main/java/com/gf/filter/SeataFilter.java b/springboot-seata/sbm-order-service/src/main/java/com/gf/filter/SeataFilter.java
new file mode 100755
index 0000000..cf6e77f
--- /dev/null
+++ b/springboot-seata/sbm-order-service/src/main/java/com/gf/filter/SeataFilter.java
@@ -0,0 +1,43 @@
+package com.gf.filter;
+
+import io.seata.core.context.RootContext;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+@Component
+public class SeataFilter implements Filter {
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest req = (HttpServletRequest) servletRequest;
+ String xid = req.getHeader(RootContext.KEY_XID.toLowerCase());
+ boolean isBind = false;
+ if (StringUtils.isNotBlank(xid)) {
+ RootContext.bind(xid);
+ isBind = true;
+ }
+ try {
+ filterChain.doFilter(servletRequest, servletResponse);
+ } finally {
+ if (isBind) {
+ RootContext.unbind();
+ }
+ }
+ }
+
+ @Override
+ public void destroy() {
+ }
+}
diff --git a/springboot-seata/sbm-order-service/src/main/java/com/gf/interceptor/SeataRestTemplateInterceptor.java b/springboot-seata/sbm-order-service/src/main/java/com/gf/interceptor/SeataRestTemplateInterceptor.java
new file mode 100755
index 0000000..ac547a0
--- /dev/null
+++ b/springboot-seata/sbm-order-service/src/main/java/com/gf/interceptor/SeataRestTemplateInterceptor.java
@@ -0,0 +1,27 @@
+package com.gf.interceptor;
+
+import io.seata.core.context.RootContext;
+import org.apache.commons.lang.StringUtils;
+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.http.client.support.HttpRequestWrapper;
+
+import java.io.IOException;
+
+public class SeataRestTemplateInterceptor implements ClientHttpRequestInterceptor {
+ public SeataRestTemplateInterceptor() {
+ }
+
+ @Override
+ public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
+ HttpRequestWrapper requestWrapper = new HttpRequestWrapper(httpRequest);
+ String xid = RootContext.getXID();
+ if (StringUtils.isNotEmpty(xid)) {
+ requestWrapper.getHeaders().add(RootContext.KEY_XID, xid);
+ }
+
+ return clientHttpRequestExecution.execute(requestWrapper, bytes);
+ }
+}
diff --git a/springboot-seata/sbm-order-service/src/main/java/com/gf/mapper/OrderMapper.java b/springboot-seata/sbm-order-service/src/main/java/com/gf/mapper/OrderMapper.java
new file mode 100755
index 0000000..ed6f317
--- /dev/null
+++ b/springboot-seata/sbm-order-service/src/main/java/com/gf/mapper/OrderMapper.java
@@ -0,0 +1,12 @@
+package com.gf.mapper;
+
+import com.gf.entity.Order;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+@Mapper
+public interface OrderMapper {
+
+ int insert(@Param("order") Order record);
+
+}
\ No newline at end of file
diff --git a/springboot-seata/sbm-order-service/src/main/java/com/gf/service/OrderService.java b/springboot-seata/sbm-order-service/src/main/java/com/gf/service/OrderService.java
new file mode 100755
index 0000000..924ab33
--- /dev/null
+++ b/springboot-seata/sbm-order-service/src/main/java/com/gf/service/OrderService.java
@@ -0,0 +1,32 @@
+package com.gf.service;
+
+import com.gf.client.AccountClient;
+import com.gf.entity.Order;
+import com.gf.mapper.OrderMapper;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+
+@Service
+@RequiredArgsConstructor(onConstructor_={@Autowired})
+public class OrderService {
+
+ private final AccountClient accountClient;
+
+ private final OrderMapper orderMapper;
+
+ public void create(String userId, String commodityCode, Integer count) {
+ BigDecimal orderMoney = new BigDecimal(count).multiply(new BigDecimal(5));
+ Order order = new Order();
+ order.setUserId(userId);
+ order.setCommodityCode(commodityCode);
+ order.setCount(count);
+ order.setMoney(orderMoney);
+
+ orderMapper.insert(order);
+ accountClient.debit(userId, orderMoney);
+
+ }
+}
\ No newline at end of file
diff --git a/springboot-seata/sbm-order-service/src/main/resources/application.properties b/springboot-seata/sbm-order-service/src/main/resources/application.properties
new file mode 100755
index 0000000..428fe1b
--- /dev/null
+++ b/springboot-seata/sbm-order-service/src/main/resources/application.properties
@@ -0,0 +1,10 @@
+spring.application.name=order-service
+server.port=8082
+spring.datasource.url=jdbc:mysql://172.16.2.101:3306/db_seata?useSSL=false&serverTimezone=UTC
+spring.datasource.username=root
+spring.datasource.password=123456
+seata.tx-service-group=my_test_tx_group
+mybatis.mapper-locations=classpath*:mapper/*Mapper.xml
+seata.service.grouplist=172.16.2.101:8091
+logging.level.io.seata=info
+logging.level.io.seata.samples.order.persistence.OrderMapper=debug
diff --git a/springboot-seata/sbm-order-service/src/main/resources/mapper/OrderMapper.xml b/springboot-seata/sbm-order-service/src/main/resources/mapper/OrderMapper.xml
new file mode 100755
index 0000000..a63398e
--- /dev/null
+++ b/springboot-seata/sbm-order-service/src/main/resources/mapper/OrderMapper.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ insert into order_tbl (user_id,commodity_code,count,money)
+ values (#{order.userId}, #{order.commodityCode}, #{order.count}, #{order.money})
+
+
+
\ No newline at end of file
diff --git a/springboot-seata/sbm-order-service/src/test/java/com/gf/SbmOrderServiceApplicationTests.java b/springboot-seata/sbm-order-service/src/test/java/com/gf/SbmOrderServiceApplicationTests.java
new file mode 100755
index 0000000..e69de29
diff --git a/springboot-seata/sbm-storage-service/.gitignore b/springboot-seata/sbm-storage-service/.gitignore
new file mode 100755
index 0000000..a2a3040
--- /dev/null
+++ b/springboot-seata/sbm-storage-service/.gitignore
@@ -0,0 +1,31 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/
diff --git a/springboot-seata/sbm-storage-service/.mvn/wrapper/MavenWrapperDownloader.java b/springboot-seata/sbm-storage-service/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100755
index 0000000..7f91a56
--- /dev/null
+++ b/springboot-seata/sbm-storage-service/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,114 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you 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.
+*/
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL =
+ "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if (mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if (mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: : " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if (!outputFile.getParentFile().exists()) {
+ if (!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output direcrory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/springboot-seata/sbm-storage-service/.mvn/wrapper/maven-wrapper.jar b/springboot-seata/sbm-storage-service/.mvn/wrapper/maven-wrapper.jar
new file mode 100755
index 0000000..01e6799
Binary files /dev/null and b/springboot-seata/sbm-storage-service/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/springboot-seata/sbm-storage-service/.mvn/wrapper/maven-wrapper.properties b/springboot-seata/sbm-storage-service/.mvn/wrapper/maven-wrapper.properties
new file mode 100755
index 0000000..cd0d451
--- /dev/null
+++ b/springboot-seata/sbm-storage-service/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip
diff --git a/springboot-seata/sbm-storage-service/pom.xml b/springboot-seata/sbm-storage-service/pom.xml
new file mode 100755
index 0000000..dc2305b
--- /dev/null
+++ b/springboot-seata/sbm-storage-service/pom.xml
@@ -0,0 +1,56 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.1.10.RELEASE
+
+
+ com.gf
+ sbm-storage-service
+ 0.0.1-SNAPSHOT
+ sbm-storage-service
+ Demo project for Spring Boot
+
+
+ 1.8
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ io.seata
+ seata-spring-boot-starter
+ 1.0.0
+
+
+ org.projectlombok
+ lombok
+
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ 2.1.0
+
+
+ mysql
+ mysql-connector-java
+ runtime
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/springboot-seata/sbm-storage-service/src/main/java/com/gf/SbmStorageServiceApplication.java b/springboot-seata/sbm-storage-service/src/main/java/com/gf/SbmStorageServiceApplication.java
new file mode 100755
index 0000000..7588a82
--- /dev/null
+++ b/springboot-seata/sbm-storage-service/src/main/java/com/gf/SbmStorageServiceApplication.java
@@ -0,0 +1,14 @@
+package com.gf;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
+
+@SpringBootApplication(scanBasePackages = "com.gf" , exclude = DataSourceAutoConfiguration.class)
+public class SbmStorageServiceApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(SbmStorageServiceApplication.class, args);
+ }
+
+}
diff --git a/springboot-seata/sbm-storage-service/src/main/java/com/gf/config/DataSourceConfig.java b/springboot-seata/sbm-storage-service/src/main/java/com/gf/config/DataSourceConfig.java
new file mode 100755
index 0000000..593c7e2
--- /dev/null
+++ b/springboot-seata/sbm-storage-service/src/main/java/com/gf/config/DataSourceConfig.java
@@ -0,0 +1,24 @@
+package com.gf.config;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import io.seata.rm.datasource.DataSourceProxy;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.mybatis.spring.SqlSessionFactoryBean;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
+
+import javax.sql.DataSource;
+
+@Configuration
+public class DataSourceConfig {
+
+ @Bean
+ @ConfigurationProperties(prefix = "spring.datasource")
+ public DataSource druidDataSource() {
+ DruidDataSource druidDataSource = new DruidDataSource();
+ return druidDataSource;
+ }
+}
\ No newline at end of file
diff --git a/springboot-seata/sbm-storage-service/src/main/java/com/gf/config/SeataRestTemplateAutoConfiguration.java b/springboot-seata/sbm-storage-service/src/main/java/com/gf/config/SeataRestTemplateAutoConfiguration.java
new file mode 100755
index 0000000..b8c3778
--- /dev/null
+++ b/springboot-seata/sbm-storage-service/src/main/java/com/gf/config/SeataRestTemplateAutoConfiguration.java
@@ -0,0 +1,47 @@
+package com.gf.config;
+
+import com.gf.interceptor.SeataRestTemplateInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.PostConstruct;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+@Configuration
+public class SeataRestTemplateAutoConfiguration {
+ @Autowired(
+ required = false
+ )
+ private Collection restTemplates;
+ @Autowired
+ private SeataRestTemplateInterceptor seataRestTemplateInterceptor;
+
+ public SeataRestTemplateAutoConfiguration() {
+ }
+
+ @Bean
+ public SeataRestTemplateInterceptor seataRestTemplateInterceptor() {
+ return new SeataRestTemplateInterceptor();
+ }
+
+ @PostConstruct
+ public void init() {
+ if (this.restTemplates != null) {
+ Iterator var1 = this.restTemplates.iterator();
+
+ while (var1.hasNext()) {
+ RestTemplate restTemplate = (RestTemplate) var1.next();
+ List interceptors = new ArrayList(restTemplate.getInterceptors());
+ interceptors.add(this.seataRestTemplateInterceptor);
+ restTemplate.setInterceptors(interceptors);
+ }
+ }
+
+ }
+}
diff --git a/springboot-seata/sbm-storage-service/src/main/java/com/gf/controller/StorageController.java b/springboot-seata/sbm-storage-service/src/main/java/com/gf/controller/StorageController.java
new file mode 100755
index 0000000..07f80ac
--- /dev/null
+++ b/springboot-seata/sbm-storage-service/src/main/java/com/gf/controller/StorageController.java
@@ -0,0 +1,26 @@
+package com.gf.controller;
+
+import com.gf.entity.Storage;
+import com.gf.service.StorageService;
+import io.seata.core.context.RootContext;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.sql.SQLException;
+
+@RequestMapping("/api/storage")
+@RestController
+@RequiredArgsConstructor(onConstructor_={@Autowired})
+public class StorageController {
+
+
+ private final StorageService storageService;
+
+ @GetMapping(value = "/deduct")
+ public void deduct(@RequestParam String commodityCode, @RequestParam Integer count) throws SQLException {
+ System.out.println("storage XID " + RootContext.getXID());
+ storageService.deduct(commodityCode, count);
+ }
+
+}
\ No newline at end of file
diff --git a/springboot-seata/sbm-storage-service/src/main/java/com/gf/entity/Storage.java b/springboot-seata/sbm-storage-service/src/main/java/com/gf/entity/Storage.java
new file mode 100755
index 0000000..8ca11b3
--- /dev/null
+++ b/springboot-seata/sbm-storage-service/src/main/java/com/gf/entity/Storage.java
@@ -0,0 +1,15 @@
+package com.gf.entity;
+
+import lombok.*;
+
+@Builder
+@Getter
+@Setter
+@ToString
+@AllArgsConstructor
+@NoArgsConstructor
+public class Storage {
+ private Integer id;
+ private String commodityCode;
+ private Integer count;
+}
diff --git a/springboot-seata/sbm-storage-service/src/main/java/com/gf/filter/SeataFilter.java b/springboot-seata/sbm-storage-service/src/main/java/com/gf/filter/SeataFilter.java
new file mode 100755
index 0000000..cf6e77f
--- /dev/null
+++ b/springboot-seata/sbm-storage-service/src/main/java/com/gf/filter/SeataFilter.java
@@ -0,0 +1,43 @@
+package com.gf.filter;
+
+import io.seata.core.context.RootContext;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+
+@Component
+public class SeataFilter implements Filter {
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest req = (HttpServletRequest) servletRequest;
+ String xid = req.getHeader(RootContext.KEY_XID.toLowerCase());
+ boolean isBind = false;
+ if (StringUtils.isNotBlank(xid)) {
+ RootContext.bind(xid);
+ isBind = true;
+ }
+ try {
+ filterChain.doFilter(servletRequest, servletResponse);
+ } finally {
+ if (isBind) {
+ RootContext.unbind();
+ }
+ }
+ }
+
+ @Override
+ public void destroy() {
+ }
+}
diff --git a/springboot-seata/sbm-storage-service/src/main/java/com/gf/interceptor/SeataRestTemplateInterceptor.java b/springboot-seata/sbm-storage-service/src/main/java/com/gf/interceptor/SeataRestTemplateInterceptor.java
new file mode 100755
index 0000000..ac547a0
--- /dev/null
+++ b/springboot-seata/sbm-storage-service/src/main/java/com/gf/interceptor/SeataRestTemplateInterceptor.java
@@ -0,0 +1,27 @@
+package com.gf.interceptor;
+
+import io.seata.core.context.RootContext;
+import org.apache.commons.lang.StringUtils;
+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.http.client.support.HttpRequestWrapper;
+
+import java.io.IOException;
+
+public class SeataRestTemplateInterceptor implements ClientHttpRequestInterceptor {
+ public SeataRestTemplateInterceptor() {
+ }
+
+ @Override
+ public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
+ HttpRequestWrapper requestWrapper = new HttpRequestWrapper(httpRequest);
+ String xid = RootContext.getXID();
+ if (StringUtils.isNotEmpty(xid)) {
+ requestWrapper.getHeaders().add(RootContext.KEY_XID, xid);
+ }
+
+ return clientHttpRequestExecution.execute(requestWrapper, bytes);
+ }
+}
diff --git a/springboot-seata/sbm-storage-service/src/main/java/com/gf/mapper/StorageMapper.java b/springboot-seata/sbm-storage-service/src/main/java/com/gf/mapper/StorageMapper.java
new file mode 100755
index 0000000..3e71ab4
--- /dev/null
+++ b/springboot-seata/sbm-storage-service/src/main/java/com/gf/mapper/StorageMapper.java
@@ -0,0 +1,23 @@
+package com.gf.mapper;
+
+import com.gf.entity.Storage;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@Mapper
+public interface StorageMapper {
+
+ Storage selectById(@Param("id") Integer id);
+
+ Storage findByCommodityCode(@Param("commodityCode") String commodityCode);
+
+ int updateById(Storage record);
+
+ void insert(Storage record);
+
+ void insertBatch(List records);
+
+ int updateBatch(@Param("list") List ids, @Param("commodityCode") String commodityCode);
+}
diff --git a/springboot-seata/sbm-storage-service/src/main/java/com/gf/service/StorageService.java b/springboot-seata/sbm-storage-service/src/main/java/com/gf/service/StorageService.java
new file mode 100755
index 0000000..f980824
--- /dev/null
+++ b/springboot-seata/sbm-storage-service/src/main/java/com/gf/service/StorageService.java
@@ -0,0 +1,23 @@
+package com.gf.service;
+
+import com.gf.entity.Storage;
+import com.gf.mapper.StorageMapper;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+@RequiredArgsConstructor(onConstructor_={@Autowired})
+public class StorageService {
+
+ private final StorageMapper storageMapper;
+
+
+ public void deduct(String commodityCode, int count) {
+ Storage storage = storageMapper.findByCommodityCode(commodityCode);
+ storage.setCount(storage.getCount() - count);
+ storageMapper.updateById(storage);
+ }
+
+
+}
diff --git a/springboot-seata/sbm-storage-service/src/main/resources/application.properties b/springboot-seata/sbm-storage-service/src/main/resources/application.properties
new file mode 100755
index 0000000..961ee43
--- /dev/null
+++ b/springboot-seata/sbm-storage-service/src/main/resources/application.properties
@@ -0,0 +1,10 @@
+spring.application.name=storage-service
+server.port=8083
+spring.datasource.url=jdbc:mysql://172.16.2.101:3306/db_seata?useSSL=false&serverTimezone=UTC
+spring.datasource.username=root
+spring.datasource.password=123456
+seata.tx-service-group=my_test_tx_group
+mybatis.mapper-locations=classpath*:mapper/*Mapper.xml
+logging.level.io.seata=info
+seata.service.grouplist=172.16.2.101:8091
+logging.level.io.seata.samples.storage.persistence.StorageMapper=debug
diff --git a/springboot-seata/sbm-storage-service/src/main/resources/mapper/StorageMapper.xml b/springboot-seata/sbm-storage-service/src/main/resources/mapper/StorageMapper.xml
new file mode 100755
index 0000000..1449b8e
--- /dev/null
+++ b/springboot-seata/sbm-storage-service/src/main/resources/mapper/StorageMapper.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ update storage_tbl set count = #{count,jdbcType=INTEGER}
+ WHERE id = #{id}
+
+
+
+ insert into storage_tbl (commodity_code, count)
+ values (#{storage.commodityCode}, #{storage.count})
+
+
+
+ insert into storage_tbl (commodity_code, count)
+ values
+
+ (#{item.commodityCode,jdbcType=VARCHAR}, #{item.count,jdbcType=INTEGER})
+
+
+
+
+ update storage_tbl set count = 100
+ WHERE id IN
+
+ #{item}
+
+ and commodity_code = #{commodityCode,jdbcType=VARCHAR}
+
+
\ No newline at end of file
diff --git a/springboot-seata/sbm-storage-service/src/test/java/com/gf/SbmStorageServiceApplicationTests.java b/springboot-seata/sbm-storage-service/src/test/java/com/gf/SbmStorageServiceApplicationTests.java
new file mode 100755
index 0000000..e69de29
diff --git a/springboot-security-oauth2/src/main/java/com/gf/config/AuthorizationServerConfiguration.java b/springboot-security-oauth2/src/main/java/com/gf/config/AuthorizationServerConfiguration.java
index 6c60339..4463400 100644
--- a/springboot-security-oauth2/src/main/java/com/gf/config/AuthorizationServerConfiguration.java
+++ b/springboot-security-oauth2/src/main/java/com/gf/config/AuthorizationServerConfiguration.java
@@ -63,26 +63,9 @@ public TokenStore tokenStore() {
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
- /**
- * 配置oauth2服务跨域
- */
- CorsConfigurationSource source = new CorsConfigurationSource() {
- @Override
- public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
- CorsConfiguration corsConfiguration = new CorsConfiguration();
- corsConfiguration.addAllowedHeader("*");
- corsConfiguration.addAllowedOrigin(request.getHeader( HttpHeaders.ORIGIN));
- corsConfiguration.addAllowedMethod("*");
- corsConfiguration.setAllowCredentials(true);
- corsConfiguration.setMaxAge(3600L);
- return corsConfiguration;
- }
- };
-
- security.tokenKeyAccess("permitAll()")
+ security.tokenKeyAccess("permitAll()")
.checkTokenAccess("permitAll()")
- .allowFormAuthenticationForClients()
- .addTokenEndpointAuthenticationFilter(new CorsFilter(source));
+ .allowFormAuthenticationForClients();
}
@Override
diff --git a/springboot-security-oauth2/src/main/java/com/gf/config/MyFilterSecurityInterceptor.java b/springboot-security-oauth2/src/main/java/com/gf/config/MyFilterSecurityInterceptor.java
index a260438..5169676 100644
--- a/springboot-security-oauth2/src/main/java/com/gf/config/MyFilterSecurityInterceptor.java
+++ b/springboot-security-oauth2/src/main/java/com/gf/config/MyFilterSecurityInterceptor.java
@@ -14,6 +14,8 @@
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@@ -32,13 +34,20 @@ public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionM
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
-
FilterInvocation fi = new FilterInvocation(servletRequest, servletResponse, filterChain);
- invoke(fi);
+ HttpServletResponse response = (HttpServletResponse)servletResponse;
+ HttpServletRequest request = (HttpServletRequest)servletRequest;
+ response.setHeader("Access-Control-Allow-Origin", "*");
+ response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
+ response.setHeader("Access-Control-Allow-Headers", ":x-requested-with,content-type");
+ filterChain.doFilter(servletRequest,servletResponse);
+ if (!request.getRequestURI().equals("/oauth/token")) {
+ invoke(fi);
+ }
+
}
public void invoke(FilterInvocation fi) throws IOException, ServletException {
-
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
//执行下一个拦截器