[
  {
    "path": ".gitignore",
    "content": "# Created by .ignore support plugin (hsz.mobi)\n### Java template\n# Compiled class file\n*.class\n\n# Log file\n*.log\n\n# BlueJ files\n*.ctxt\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Package Files #\n*.jar\n*.war\n*.nar\n*.ear\n*.zip\n*.tar.gz\n*.rar\n\n# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml\nhs_err_pid*\n\nSpringBoot-Labs.ipr\nSpringBoot-Labs.iws\ntarget/**\n*.lst\n*.iml\n*ipr\n*.iws\n\n### IntelliJ IDEA\n.idea\ntarget"
  },
  {
    "path": "README.md",
    "content": "> 友情提示：因为提供了 50000+ 行示例代码，所以艿艿默认注释了所有 Maven Module。\n>\n> 胖友可以根据自己的需要，修改 [`pom.xml`](https://github.com/YunaiV/SpringBoot-Labs/blob/master/pom.xml) 即可。\n\n一个涵盖六个主流技术栈的**正经**仓库：\n* [《Spring Boot 专栏》](https://github.com/YunaiV/SpringBoot-Labs#spring-boot-%E4%B8%93%E6%A0%8F)\n* [《Spring Cloud Alibaba 专栏》](https://github.com/YunaiV/SpringBoot-Labs#spring-cloud-alibaba-%E4%B8%93%E6%A0%8F)\n* [《Spring Cloud 专栏》](https://github.com/YunaiV/SpringBoot-Labs#spring-cloud-%E4%B8%93%E6%A0%8F)\n* [《Dubbo 专栏》](https://github.com/YunaiV/SpringBoot-Labs#Dubbo-%E4%B8%93%E6%A0%8F)\n* [《消息队列 MQ 专栏》](https://github.com/YunaiV/SpringBoot-Labs#%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97-mq-%E4%B8%93%E6%A0%8F)\n* [《分布式事务专栏》](https://github.com/YunaiV/SpringBoot-Labs#分布式事务专栏)\n\n作为一个热爱**深夜**撸码的 18 岁头发茂密的可爱小男孩，希望大佬能够**一键三连**。\n\n![一间三连](http://static.iocoder.cn/github-star.jpg)\n\n> **亲，一键三连啊**\n\n# 交流群\n\n扫码如下**二维码**，回复「艿艿」关键字。\n\n可以添加 18 岁的我为好友，并拉你进一个~~交流~~**装逼群**。\n\n![骚气的二维码](http://www.iocoder.cn/images/common/erweima.jpg)\n\n😈 等后面，艿艿**头发**重新长一点出来，给旁友们录制点视频哈！\n\n# Spring Boot 专栏\n\n基于 Spring Boot 2.X 版本的**深度**入门教程。\n\n市面上的 Spring Boot **基础**入门文章很多，但是**深度**入门文章却很少。对于很多开发者来说，入门即是其对某个技术栈的最终理解，一方面是开发者“比较懒”，另一方面是文章作者把 Spring Boot 入门写的太浅，又或者不够全面。\n\n因此，艿艿开始了这个 Spring Boot 专栏，一个**深度**且**全面**的 Spring Boot 2.X 入门。\n* 在带你快速学会 SpringMVC API 接口的编写的同时，我还想告诉你还有全局返回、全局异常、拦截器、跨域处理等等功能。\n* 在带你快速学会 MQ 消息的发送与消费的同时，我还想告诉你 MQ 还有集群消费、广播消费、顺序消息、定时消息、事务消息、消费重试等等特性。\n* 在带你快速学会 Job 任务的编写的同时，我还想告诉你还有 Quartz 单体、Quartz 集群、XXL-JOB 等等企业使用更多的调度平台。\n* ...\n\n让我们一起愉快的挖坑，挖深坑，哇哈哈。\n\n## 打好基础\n\n* [《芋道 Spring Boot 快速入门》](http://www.iocoder.cn/Spring-Boot/quick-start/?github)\n* [《芋道 Spring Boot 自动配置原理》](http://www.iocoder.cn/Spring-Boot/autoconfigure/?github) 对应 [lab-47](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-47)\n* [《芋道 Spring Boot Jar 启动原理》](http://www.iocoder.cn/Spring-Boot/jar/?github)\n* [《芋道 Spring Boot 调试环境》](http://www.iocoder.cn/Spring-Boot/build-debugging-environment-2-6-0/?github)\n\n## 开发工具\n\n* [《芋道 Spring Boot 热部署入门》](http://www.iocoder.cn/Spring-Boot/hot-swap/?github) 对应 [lab-48-hot-swap](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-48-hot-swap)\n* [《芋道 Spring Boot 消除冗余代码 Lombok 入门》](http://www.iocoder.cn/Spring-Boot/Lombok/?github) 对应 [lab-49](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-49)\n* [《芋道 Spring Boot 对象转换 MapStruct 入门》](http://www.iocoder.cn/Spring-Boot/MapStruct/?github) 对应 [lab-55](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-55)\n\n## Web 开发\n\n* [《芋道 Spring Boot SpringMVC 入门》](http://www.iocoder.cn/Spring-Boot/SpringMVC/?github) 对应 [lab-23](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-23)\n* [《芋道 Spring Boot WebFlux 入门》](http://www.iocoder.cn/Spring-Boot/WebFlux/?github) 对应 [lab-27](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-27)\n* [《芋道 Spring Boot 分布式 Session 入门》](http://www.iocoder.cn/Spring-Boot/Distributed-Session/?github) 对应 [lab-26](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-26)\n* [《芋道 Spring Boot API 接口文档 Swagger 入门》](http://www.iocoder.cn/Spring-Boot/Swagger/?github) 对应 [lab-24](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-24)\n* [《芋道 Spring Boot API 接口文档 Swagger Starter 入门》](http://www.iocoder.cn/Spring-Boot/Swagger-Starter/?github) 对应 [lab-24](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-24)\n* [《芋道 Spring Boot 参数校验 Validation 入门》](http://www.iocoder.cn/Spring-Boot/Validation/?github) 对应 [lab-22](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-22)\n* [《芋道 Spring Boot WebSocket 入门》](http://www.iocoder.cn/Spring-Boot/WebSocket/?github) 对应 [lab-25](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-25)\n* [《性能测试 —— Tomcat、Jetty、Undertow 基准测试》](http://www.iocoder.cn/Performance-Testing/Tomcat-Jetty-Undertow-benchmark/?github) 对应 [lab-05-benchmark-tomcat-jetty-undertow](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-05-benchmark-tomcat-jetty-undertow)\n* [《性能测试 —— SpringMVC、Webflux 基准测试》](http://www.iocoder.cn/Performance-Testing/SpringMVC-Webflux-benchmark/?github) 对应 [lab-06](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-06)\n* [《芋道 Spring Boot API 接口文档 JApiDocs 入门》](http://www.iocoder.cn/Spring-Boot/JApiDocs/?github) 对应 [lab-24](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-24)\n* [《芋道 Spring Boot API 接口文档 ShowDoc 入门》](http://www.iocoder.cn/Spring-Boot/ShowDoc/?github) 对应 [lab-24](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-24)\n* [《芋道 Spring Boot API 接口调试 IDEA HTTP Client》](http://www.iocoder.cn/Spring-Boot/IDEA-HTTP-Client/?github) 对应 [lab-71-http-debug](https://github.com/YunaiV/SpringBoot-Labs/blob/master/lab-71-http-debug/)\n\n## RPC 开发\n\n* [《芋道 Spring Boot Netty 入门》](http://www.iocoder.cn/Spring-Boot/Netty/?github) 对应 [lab-67](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-67)\n* [《芋道 Spring Boot Dubbo 入门》](http://www.iocoder.cn/Spring-Boot/Dubbo/?github) 对应 [lab-30](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-30)\n* [《芋道 Spring Boot 声明式调用 Feign 入门》](http://www.iocoder.cn/Spring-Boot/Feign/?github) 对应 [lab-58](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-58)\n* [《芋道 Spring Boot gRPC 入门》](http://www.iocoder.cn/Spring-Boot/gRPC/?github) 对应 [lab-64](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-64)\n* [《芋道 Spring Boot Web Services 入门》](http://www.iocoder.cn/Spring-Boot/Web-Services/?github) 对应 [lab-65](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-65)\n* [《芋道 Spring Boot SOFARPC 入门》](http://www.iocoder.cn/Spring-Boot/SOFARPC/?github) 对应 [lab-62](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-62)\n* [《芋道 Spring Boot Motan 入门》](http://www.iocoder.cn/Spring-Boot/Motan/?github) 对应 [lab-63](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-63)\n* 《芋道 Spring Boot RSocket 入门》计划中...\n* 《芋道 Spring Boot Tars 入门》计划中...\n\n## 文件存储\n\n* [《芋道 Spring Boot 对象存储 MinIO 入门》](https://www.iocoder.cn/Spring-Boot/MinIO/?github) 对应 [lab-72-minio](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-72-minio)\n\n## 数据访问\n\n**关系数据库**\n\n* [《芋道 Spring Boot 数据库连接池入门》](http://www.iocoder.cn/Spring-Boot/datasource-pool/?github) 对应 [lab-19](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-19)\n* [《芋道 Spring Boot MyBatis 入门》](http://www.iocoder.cn/Spring-Boot/MyBatis/?github) 对应 [lab-12-mybatis](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-12-mybatis)\n* [《芋道 Spring Boot JPA 入门》](http://www.iocoder.cn/Spring-Boot/JPA/?github) 对应 [lab-13-spring-data-jpa](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-13-spring-data-jpa)\n* [《芋道 Spring Boot JdbcTemplate 入门》](http://www.iocoder.cn/Spring-Boot/JdbcTemplate/?github) 对应 [lab-14-spring-jdbc-template](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-14-spring-jdbc-template)\n* [《芋道 Spring Boot 多数据源（读写分离）入门》](http://www.iocoder.cn/Spring-Boot/dynamic-datasource/?github) 对应 [lab-17](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-17)\n* [《芋道 Spring Boot 分库分表入门》](http://www.iocoder.cn/Spring-Boot/sharding-datasource/?github) 对应 [lab-18](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-18)\n* [《芋道 Spring Boot 数据库版本管理入门》](http://www.iocoder.cn/Spring-Boot/database-version-control/?github) 对应 [lab-20](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-20)\n* [《芋道 Spring Boot 数据表结构文档》](http://www.iocoder.cn/Spring-Boot/DB-Doc-screw/?github) 对应 [lab-70-db-doc](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-70-db-doc)\n\n**非关系数据库**\n\n* [《芋道 Spring Boot Redis 入门》](http://www.iocoder.cn/Spring-Boot/Redis/?github) 对应 [lab-11-spring-data-redis](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-11-spring-data-redis)\n* [《芋道 Spring Boot 缓存 Cache 入门》](http://www.iocoder.cn/Spring-Boot/Cache/?github) 对应 [lab-21](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-21)\n* [《芋道 Spring Boot MongoDB 入门》](http://www.iocoder.cn/Spring-Boot/MongoDB/?github) 对应 [lab-16-spring-data-mongo](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-16-spring-data-mongo)\n* [《芋道 Spring Boot Elasticsearch 入门》](http://www.iocoder.cn/Spring-Boot/Elasticsearch/?github) 对应 [lab-15-spring-data-es](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-15-spring-data-es)\n* [《芋道 Spring Boot Solr 入门》](http://www.iocoder.cn/Spring-Boot/Solr/?github) 对应 [lab-66](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-66)\n\n## 事务管理\n\n* [《芋道 Spring Boot 分布式事务 Seata 入门》](http://www.iocoder.cn/Spring-Boot/Seata/?github) 对应 [lab-52](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-52)\n\n## 安全控制\n\n* [《芋道 Spring Boot 安全框架 Spring Security 入门》](http://www.iocoder.cn/Spring-Boot/Spring-Security/?github) 对应 [lab-01-spring-security](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-01-spring-security)\n* [《芋道 Spring Boot 安全框架 Shiro 入门》](http://www.iocoder.cn/Spring-Boot/Shiro/?github) 对应 [lab-33](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-33)\n\n**OAuth 2.0**\n* [《芋道 Spring Security OAuth2 入门》](http://www.iocoder.cn/Spring-Security/OAuth2-learning/?github) 对应 [lab-68-spring-security-oauth](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-68-spring-security-oauth)\n* [《芋道 Spring Security OAuth2 存储器》](http://www.iocoder.cn/Spring-Security/OAuth2-learning-store/?github) 对应 [lab-68-spring-security-oauth](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-68-spring-security-oauth)\n* [《芋道 Spring Security OAuth2 单点登陆》](http://www.iocoder.cn/Spring-Security/OAuth2-learning-sso/?github) 对应 [lab-68-spring-security-oauth](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-68-spring-security-oauth)\n\n## 定时任务与异步任务\n\n* [《芋道 Spring Boot 定时任务入门》](http://www.iocoder.cn/Spring-Boot/Job/?github) 对应 [lab-28](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-28)\n* [《芋道 Spring Boot 异步任务入门》](http://www.iocoder.cn/Spring-Boot/Async-Job/?github) 对应 [lab-29](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-29)\n\n## 消息队列\n\n* [《芋道 Spring Boot 消息队列 RocketMQ 入门》](http://www.iocoder.cn/Spring-Boot/RocketMQ/?github) 对应 [lab-31](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-31)\n* [《芋道 Spring Boot 消息队列 Kafka 入门》](http://www.iocoder.cn/Spring-Boot/Kafka/?github) 对应 [lab-03-kafka](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-03-kafka)\n* [《芋道 Spring Boot 消息队列 RabbitMQ 入门》](http://www.iocoder.cn/Spring-Boot/RabbitMQ/?github) 对应 [lab-04-rabbitmq](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-04-rabbitmq)\n* [《芋道 Spring Boot 消息队列 ActiveMQ 入门》](http://www.iocoder.cn/Spring-Boot/ActiveMQ/?github) 对应 [lab-32](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-32)\n* [《芋道 Spring Boot 事件机制 Event 入门》](http://www.iocoder.cn/Spring-Boot/Event/?github) 对应 对应 [lab-54](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-54)\n\n## 配置中心\n\n* [《芋道 Spring Boot 配置文件入门》](http://www.iocoder.cn/Spring-Boot/config-file/?github) 对应 [lab-43](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-43)\n* [《芋道 Spring Boot 配置中心 Apollo 入门》](http://www.iocoder.cn/Spring-Boot/config-apollo/?github) 对应 [lab-45](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-45)\n* [《芋道 Spring Boot 配置中心 Nacos 入门》](http://www.iocoder.cn/Spring-Boot/config-nacos/?github) 对应 [lab-44](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-44)\n\n## 注册中心\n\n* [《芋道 Spring Boot 注册中心 Nacos 入门》](http://www.iocoder.cn/Spring-Boot/registry-nacos/?github) 对应 [lab-44](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-44)\n\n## 持续交付\n\n* [《芋道 Spring Boot 持续交付 Jenkins 入门》](http://www.iocoder.cn/Spring-Boot/Jenkins/?github) 对应 [lab-41](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-41)\n* [《芋道 Spring Boot 单元测试 Test 入门》](http://www.iocoder.cn/Spring-Boot/Unit-Test/?github) 对应 [lab-42](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-42)\n* 《芋道 Spring Boot 容器 Docker 入门》计划中...\n\n## 服务容错\n\n* [《芋道 Spring Boot 服务容错 Sentinel 入门》](http://www.iocoder.cn/Spring-Boot/Sentinel/?github) 对应 [lab-46](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-46)\n* [《芋道 Spring Boot 服务容错 Hystrix 入门》](http://www.iocoder.cn/Spring-Boot/Hystrix/?github) 对应 [lab-57](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-57)\n* [《芋道 Spring Boot 服务容错 Resilience4j 入门》](http://www.iocoder.cn/Spring-Boot/Resilience4j/?github) 对应 [lab-59](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-59)\n\n## 监控管理\n\n* [《芋道 Spring Boot 异常管理平台 Sentry 入门》](http://www.iocoder.cn/Spring-Boot/Sentry/?github) 对应 [lab-51](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-51)\n* [《芋道 Spring Boot 监控端点 Actuator 入门》](http://www.iocoder.cn/Spring-Boot/Actuator/?github) 对应 [lab-34](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-34)\n* [《芋道 Spring Boot 监控工具 Admin 入门》](http://www.iocoder.cn/Spring-Boot/Admin/?github) 对应 [lab-35](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-35)\n* [《芋道 Spring Boot 监控平台 Prometheus + Grafana 入门》](http://www.iocoder.cn/Spring-Boot/Prometheus-and-Grafana/?github) 对应 [lab-36](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-36)\n* [《芋道 Spring Boot 监控平台 CAT 入门》](http://www.iocoder.cn/Spring-Boot/CAT/?github) 对应 [lab-61](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-61)\n\n## 日志管理\n\n* [《芋道 Spring Boot 日志集成 Logging 入门》](http://www.iocoder.cn/Spring-Boot/Logging/?github) 对应 [lab-37](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-37)\n* [《芋道 Spring Boot 日志平台 ELK + Filebeat 入门》](http://www.iocoder.cn/Spring-Boot/ELK/?github) 对应 [lab-38](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-38)\n* 《芋道 Spring Boot 日志平台 Loki 入门》计划中...\n\n## 链路追踪\n\n* [《芋道 Spring Boot 链路追踪 SkyWalking 入门》](http://www.iocoder.cn/Spring-Boot/SkyWalking/?github) 对应 [lab-39](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-39)\n* [《芋道 Spring Boot 链路追踪 Zipkin 入门》](http://www.iocoder.cn/Spring-Boot/Zipkin/?github) 对应 [lab-40](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-40)\n* 《芋道 Spring Boot 链路追踪 Pinpoint 入门》计划中...\n* 《芋道 Spring Boot 链路追踪 Elastic APM 入门》计划中...\n\n# Spring Cloud Alibaba 专栏\n\n## Spring Cloud Alibaba 全家桶\n\n* [《芋道 Spring Cloud Alibaba 介绍》](http://www.iocoder.cn/Spring-Cloud-Alibaba/intro/?github)\n* [《芋道 Spring Cloud Alibaba 注册中心 Nacos 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Nacos-Discovery/?github) 对应 [labx-01-spring-cloud-alibaba-nacos-discovery](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-01-spring-cloud-alibaba-nacos-discovery)\n* [《芋道 Spring Cloud Alibaba 服务调用 Dubbo 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Dubbo/?github) 对应 [labx-07-spring-cloud-alibaba-dubbo](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-07-spring-cloud-alibaba-dubbo)\n* [《芋道 Spring Cloud Alibaba 服务容错 Sentinel 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Sentinel/?github) 对应 [labx-04-spring-cloud-alibaba-sentinel](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-04-spring-cloud-alibaba-sentinel)\n* [《芋道 Spring Cloud Alibaba 消息队列 RocketMQ 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/RocketMQ/?github) 对应 [labx-06-spring-cloud-stream-rocketmq](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-06-spring-cloud-stream-rocketmq)\n* [《芋道 Spring Cloud Alibaba 事件总线 Bus RocketMQ 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Bus-RocketMQ/?github) 对应 [labx-20](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-20)\n* [《芋道 Spring Cloud Alibaba 配置中心 Nacos 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Nacos-Config/?github) 对应 [labx-05-spring-cloud-alibaba-nacos-config](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-05-spring-cloud-alibaba-nacos-config)\n* [《芋道 Spring Cloud Alibaba 分布式事务 Seata 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Seata/?github) 对应 [labx-17](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-17)\n\n## 推荐搭配食用\n\n* [《芋道 Spring Cloud Netflix 负载均衡 Ribbon 入门》](http://www.iocoder.cn/Spring-Cloud-Netflix/Ribbon/?github) 对应 [labx-02-spring-cloud-netflix-ribbon](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-02-spring-cloud-netflix-ribbon)\n* [《芋道 Spring Cloud 声明式调用 Feign 入门》](http://www.iocoder.cn/Spring-Cloud/Feign/?github) 对应 [labx-03-spring-cloud-feign](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-03-spring-cloud-feign)\n* [《芋道 Spring Cloud 服务网关 Spring Cloud Gateway 入门》](http://www.iocoder.cn/Spring-Cloud/Spring-Cloud-Gateway/?github) 对应 [labx-08-spring-cloud-gateway](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-08-spring-cloud-gateway)\n* [《芋道 Spring Cloud 链路追踪 SkyWalking 入门》](http://www.iocoder.cn/Spring-Cloud/SkyWalking/?github) 对应 [labx-14](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-14)\n* [《芋道 Dubbo Admin 快速入门》](http://www.iocoder.cn/Dubbo/Admin/?github)\n* [《芋道 Dubbo Swagger 快速入门》](http://www.iocoder.cn/Dubbo/Swagger/?github) 对应 [swagger-dubbo](https://github.com/YunaiV/swagger-dubbo)\n \n# Spring Cloud 专栏\n\n## 注册中心\n\n* [《芋道 Spring Cloud Alibaba 注册中心 Nacos 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Nacos-Discovery/?github) 对应 [labx-01-spring-cloud-alibaba-nacos-discovery](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-01-spring-cloud-alibaba-nacos-discovery)\n* [《芋道 Spring Cloud Netflix 注册中心 Eureka 入门》](http://www.iocoder.cn/Spring-Cloud/Netflix-Eureka?github) 对应 [labx-22](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-22)\n* [《芋道 Spring Cloud 注册中心 Zookeeper 入门》](http://www.iocoder.cn/Spring-Cloud/ZooKeeper-Discovery/?github) 对应 [labx-25](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-25)\n* [《芋道 Spring Cloud 注册中心 Consul 入门》](http://www.iocoder.cn/Spring-Cloud/Consul-Discovery/?github) 对应 [labx-27](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-27)\n* [《芋道 Spring Cloud 注册中心 Etcd 入门》](http://www.iocoder.cn/Spring-Cloud/Etcd/?github)\n\n## 服务调用\n\n* [《芋道 Spring Cloud Alibaba 服务调用 Dubbo 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Dubbo/?github) 对应 [labx-07-spring-cloud-alibaba-dubbo](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-07-spring-cloud-alibaba-dubbo)\n* [《芋道 Spring Cloud Netflix 负载均衡 Ribbon 入门》](http://www.iocoder.cn/Spring-Cloud-Netflix/Ribbon/?github) 对应 [labx-02-spring-cloud-netflix-ribbon](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-02-spring-cloud-netflix-ribbon)\n* [《芋道 Spring Cloud 声明式调用 Feign 入门》](http://www.iocoder.cn/Spring-Cloud/Feign/?github) 对应 [labx-03-spring-cloud-feign](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-03-spring-cloud-feign)\n* [《芋道 Spring Cloud 服务调用 gRPC 入门》](http://www.iocoder.cn/Spring-Cloud/gRPC/?github) 对应 [labx-30-spring-cloud-grpc](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-30-spring-cloud-grpc)\n\n## 服务容错\n\n* [《芋道 Spring Cloud Alibaba 服务容错 Sentinel 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Sentinel/?github) 对应 [labx-04-spring-cloud-alibaba-sentinel](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-04-spring-cloud-alibaba-sentinel)\n* [《芋道 Spring Cloud Netflix 服务容错 Hystrix 入门》](http://www.iocoder.cn/Spring-Cloud/Netflix-Hystrix/?github) 对应 [labx-23](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-23)\n* [《芋道 Spring Cloud 服务容错 Resilience4j 入门》](http://www.iocoder.cn/Spring-Cloud/Resilience4j/?github) 对应 [lab-59](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-59)\n* 《芋道 Spring Cloud 服务容错 Spring Cloud CircuitBreaker》计划中...\n\n## API 网关\n\n* [《芋道 Spring Cloud 服务网关 Spring Cloud Gateway 入门》](http://www.iocoder.cn/Spring-Cloud/Spring-Cloud-Gateway/?github) 对应 [labx-08-spring-cloud-gateway](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-08-spring-cloud-gateway)\n* [《芋道 Spring Cloud Netflix 服务网关 Zuul 入门》](http://www.iocoder.cn/Spring-Cloud/Netflix-Zuul/?github) 对应 对应 [labx-21](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-21)\n* [《性能测试 —— Spring Cloud Gateway、Zuul 基准测试》](http://www.iocoder.cn/Performance-Testing/SpringCloudGateway-Zuul-benchmark/?github) 对应 [lab-07](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-07)\n\n> 如下非 Spring Cloud 网关，先放在这里...\n\n* [《芋道 APISIX 极简入门（国产微服务网关）》](http://www.iocoder.cn/APISIX/install/?github) 对应 [lab-56](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-56)\n* [《芋道 Soul 极简入门（国产微服务网关）》](http://www.iocoder.cn/Soul/install/?github) 对应 [lab-60](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-60)\n* [《芋道 Kong 极简入门（微服务网关）》](http://www.iocoder.cn/Kong/install/?github) 对应 [lab-56](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-56)\n\n## 配置中心\n\n* [《芋道 Spring Cloud Alibaba 配置中心 Nacos 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Nacos-Config/?github) 对应 [labx-05-spring-cloud-alibaba-nacos-config](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-05-spring-cloud-alibaba-nacos-config)\n* [《芋道 Spring Cloud 配置中心 Apollo 入门》](http://www.iocoder.cn/Spring-Cloud/Apollo/?github) 对应 [labx-09-spring-cloud-apollo](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-09-spring-cloud-apollo)\n* [《芋道 Spring Cloud 配置中心 Spring Cloud Config 入门》](http://www.iocoder.cn/Spring-Cloud/Spring-Cloud-Config/?github) 对应 [labx-12-spring-cloud-config](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-12-spring-cloud-config)\n* [《芋道 Spring Cloud 配置中心 Zookeeper 入门》](http://www.iocoder.cn/Spring-Cloud/ZooKeeper-Config/?github) 对应 [labx-26](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-26)\n* [《芋道 Spring Cloud 配置中心 Consul 入门》](http://www.iocoder.cn/Spring-Cloud/Consul-Config/?github) 对应 [labx-28](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-28)\n* [《芋道 Spring Cloud 配置中心 Etcd 入门》](http://www.iocoder.cn/Spring-Cloud/Etcd/?github)\n\n## 消息队列\n\n**Spring Cloud Stream**\n* [《芋道 Spring Cloud Alibaba 消息队列 RocketMQ 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/RocketMQ/?github) 对应 [labx-06-spring-cloud-stream-rocketmq](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-06-spring-cloud-stream-rocketmq)\n* [《芋道 Spring Cloud 消息队列 RabbitMQ 入门》](http://www.iocoder.cn/Spring-Cloud/RabbitMQ/?github) 对应 [labx-10-spring-cloud-stream-rabbitmq](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-10-spring-cloud-stream-rabbitmq)\n* [《芋道 Spring Cloud 消息队列 Kafka 入门》](http://www.iocoder.cn/Spring-Cloud/Kafka/?github) 对应 [labx-11-spring-cloud-stream-kafka](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-11-spring-cloud-stream-kafka)\n* [《芋道 Spring Cloud 消息队列 ActiveMQ 入门》](http://www.iocoder.cn/Spring-Cloud/ActiveMQ/?github)\n\n**Spring Cloud Bus**\n* [《芋道 Spring Cloud Alibaba 事件总线 Bus RocketMQ 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Bus-RocketMQ/?github) 对应 [labx-20](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-20)\n* [《芋道 Spring Cloud 事件总线 Bus RabbitMQ 入门》](http://www.iocoder.cn/Spring-Cloud/Bus-RabbitMQ/?github) 对应 [labx-19](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-19)\n* [《芋道 Spring Cloud 事件总线 Bus Kafka 入门》](http://www.iocoder.cn/Spring-Cloud/Bus-Kafka/?github) 对应 [labx-18](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-18)\n* [《芋道 Spring Cloud 事件总线 Bus Consul 入门》](http://www.iocoder.cn/Spring-Cloud/Bus-Consul/?github) 对应 [labx-29-spring-cloud-consul-bus](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-29-spring-cloud-consul-bus)\n\n## 分布式事务\n\n* [《芋道 Spring Cloud Alibaba 分布式事务 Seata 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Seata/?github) 对应 [labx-17](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-17)\n\n## 监控管理\n\n* [《芋道 Spring Boot 异常管理平台 Sentry 入门》](http://www.iocoder.cn/Spring-Boot/Sentry/?github) 对应 [lab-51](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-51)\n* [《芋道 Spring Boot 监控端点 Actuator 入门》](http://www.iocoder.cn/Spring-Boot/Actuator/?github) 对应 [lab-34](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-34)\n* [《芋道 Spring Cloud 监控工具 Admin 入门》](http://www.iocoder.cn/Spring-Cloud/SB-Admin/?github) 对应 [labx-15](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-15)\n* [《芋道 Spring Boot 监控平台 Prometheus + Grafana 入门》](http://www.iocoder.cn/Spring-Boot/Prometheus-and-Grafana/?github) 对应 [lab-36](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-36)\n\n## 持续交付\n\n* [《芋道 Spring Cloud 持续交付 Jenkins 入门》](http://www.iocoder.cn/Spring-Cloud/Jenkins/?github) 对应 [labx-16](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-16)\n* [《芋道 Spring Boot 单元测试 Test 入门》](http://www.iocoder.cn/Spring-Boot/Unit-Test/?github) 对应 [lab-42](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-42)\n* 《芋道 Spring Cloud 容器 Docker 入门》计划中...\n\n## 链路追踪\n\n* [《芋道 Spring Cloud 链路追踪 SkyWalking 入门》](http://www.iocoder.cn/Spring-Cloud/SkyWalking/?github) 对应 [labx-14](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-14)\n* [《芋道 Spring Cloud 链路追踪 Spring Cloud Sleuth》](http://www.iocoder.cn/Spring-Cloud/Spring-Cloud-Sleuth/?github) 对应 [labx-13](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-13)\n\n# Dubbo 专栏\n\n## 基础入门\n\n* [《芋道 Spring Boot Dubbo 入门》](http://www.iocoder.cn/Spring-Boot/Dubbo/?github) 对应 [lab-30](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-30)\n* [《芋道 Spring Cloud Alibaba 服务调用 Dubbo 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Dubbo/?github) 对应 [labx-07-spring-cloud-alibaba-dubbo](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-07-spring-cloud-alibaba-dubbo)\n* [《性能测试 —— Dubbo 基准测试》](http://www.iocoder.cn/Performance-Testing/Dubbo-benchmark/?github)\n* [《芋道 Dubbo Swagger 快速入门》](http://www.iocoder.cn/Dubbo/Swagger/?github) 对应 [swagger-dubbo](https://github.com/YunaiV/swagger-dubbo)\n\n## 注册中心\n\n**[Zookeeper](http://www.iocoder.cn/Zookeeper/install/?github)**\n* [《芋道 Spring Boot Dubbo 入门》](http://www.iocoder.cn/Spring-Boot/Dubbo/?github)的[「2. XML 配置」](#)和[「3. 注解配置」](#)小节\n\n**[Nacos](http://www.iocoder.cn/Nacos/install/?github)**\n* [《芋道 Spring Boot Dubbo 入门》](http://www.iocoder.cn/Spring-Boot/Dubbo/?github)的[「6. 整合 Nacos」](#)小节\n* [《芋道 Spring Cloud Alibaba 服务调用 Dubbo 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Dubbo/?github)的[「2. 快速入门」](#)小节\n\n## 服务容错\n\n**[Sentinel](http://www.iocoder.cn/Sentinel/install/?github)**\n* [《芋道 Spring Boot Dubbo 入门》](http://www.iocoder.cn/Spring-Boot/Dubbo/?github)的[「7. 整合 Sentinel」](#)小节\n* [《芋道 Spring Cloud Alibaba 服务调用 Dubbo 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Dubbo/?github)的[「6. 整合 Sentinel」](#)小节\n\n**[Hystrix](http://www.iocoder.cn/categories/Hystrix/?github)**\n* [《芋道 Spring Boot 服务容错 Hystrix 入门》](http://www.iocoder.cn/Spring-Boot/Hystrix/?github)的[「6. 集成到 Dubbo」](#)小节\n* [《芋道 Spring Cloud Netflix 服务容错 Hystrix 入门》](http://www.iocoder.cn/Spring-Cloud/Netflix-Hystrix/?github)的[「10. 集成到 Dubbo」](#)小节\n\n**[Resilience4j](http://www.iocoder.cn/categories/Resilience4j/?github)**\n* [《芋道 Spring Boot 服务容错 Resilience4j 入门》](http://www.iocoder.cn/Spring-Boot/Resilience4j/?github)的[「10. 集成到 Dubbo」](#)小节\n\n## API 网关\n\n* [《芋道 Soul 极简入门（国产微服务网关）》](http://www.iocoder.cn/Soul/install/?github)的[「3. 接入 Dubbo 应用」](#)小节\n\n## 分布式事务\n\n**[Seata](http://www.iocoder.cn/Seata/install/?github)**\n* [《芋道 Dubbo 分布式事务 Seata 入门》](http://www.iocoder.cn/Dubbo/Seata/?github) 对应 [lab-53](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-53)\n* [《芋道 Spring Cloud Alibaba 分布式事务 Seata 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Seata/?github)的[「2. AT 模式 + Dubbo」](#)小节\n\n**[TCC Transaction](http://www.iocoder.cn/categories/TCC-Transaction/?github)**\n* [《TCC-Transaction 源码分析 —— Dubbo 支持》](http://www.iocoder.cn/TCC-Transaction/dubbo-support/?self)\n\n## 链路追踪\n\n**[SkyWalking](http://www.iocoder.cn/SkyWalking/install/?github)**\n* [《芋道 Spring Boot 链路追踪 SkyWalking 入门》](http://www.iocoder.cn/Spring-Boot/SkyWalking/?github) 的[「16. Dubbo 示例」](#)小节\n* [《芋道 Spring Cloud 链路追踪 SkyWalking 入门》](http://www.iocoder.cn/Spring-Cloud/SkyWalking/?github) 的[「7. Dubbo 示例」](#)小节\n\n**[Zipkin](http://www.iocoder.cn/Zipkin/install/?github)**\n* [《芋道 Spring Boot 链路追踪 Zipkin》](http://www.iocoder.cn/Spring-Cloud/Spring-Cloud-Sleuth/?github) 的[「13. Dubbo 示例」](#)小节\n* [《芋道 Spring Cloud 链路追踪 Spring Cloud Sleuth》](http://www.iocoder.cn/Spring-Cloud/Spring-Cloud-Sleuth/?github) 的[「7. Dubbo 示例」](#)小节\n\n## 监控管理\n\n**[CAT](http://www.iocoder.cn/CAT/install/?github)**\n* [《芋道 Spring Boot 监控平台 CAT 入门》](http://www.iocoder.cn/Spring-Boot/CAT/?github) 的[「13. Dubbo 集成」](#)小节\n\n**[Dubbo Admin](http://www.iocoder.cn/Dubbo/Admin/?github)**\n* [《芋道 Dubbo Admin 快速入门》](http://www.iocoder.cn/Dubbo/Admin/?github)\n\n# 消息队列 MQ 专栏\n\n## RocketMQ\n\n* [《RocketMQ 极简入门》](http://www.iocoder.cn/RocketMQ/install/?github)\n* [《芋道 Spring Boot 消息队列 RocketMQ 入门》](http://www.iocoder.cn/Spring-Boot/RocketMQ/?github) 对应 [lab-31](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-31)\n* [《芋道 Spring Cloud Alibaba 消息队列 RocketMQ 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/RocketMQ/?github) 对应 [labx-06-spring-cloud-stream-rocketmq](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-06-spring-cloud-stream-rocketmq)\n* [《芋道 Spring Cloud Alibaba 事件总线 Bus RocketMQ 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Bus-RocketMQ/?github) 对应 [labx-20](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-20)\n* [《芋道 RocketMQ 源码解析系列》](http://www.iocoder.cn/categories/RocketMQ/?github)\n* [《性能测试 —— RocketMQ 基准测试》](http://www.iocoder.cn/Performance-Testing/RocketMQ-benchmark/?github)\n* [《RocketMQ 书单整理》](http://www.iocoder.cn/Books/RocketMQ-books-recommended/?github)\n\n## RabbitMQ\n\n* [《RabbitMQ 极简入门》](http://www.iocoder.cn/RabbitMQ/install/?github)\n* [《芋道 Spring Boot 消息队列 RabbitMQ 入门》](http://www.iocoder.cn/Spring-Boot/RabbitMQ/?github) 对应 [lab-04-rabbitmq](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-04-rabbitmq)\n* [《芋道 Spring Cloud 消息队列 RabbitMQ 入门》](http://www.iocoder.cn/Spring-Cloud/RabbitMQ/?github) 对应 [labx-10-spring-cloud-stream-rabbitmq](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-10-spring-cloud-stream-rabbitmq)\n* [《芋道 Spring Cloud 事件总线 Bus RabbitMQ 入门》](http://www.iocoder.cn/Spring-Cloud/Bus-RabbitMQ/?github) 对应 [labx-19](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-19)\n* [《RabbitMQ 书单整理》](http://www.iocoder.cn/Books/RabbitMQ-books-recommended/?github)\n\n## Kafka\n\n* [《Kafka 极简入门》](http://www.iocoder.cn/Kafka/install/?github)\n* [《芋道 Spring Boot 消息队列 Kafka 入门》](http://www.iocoder.cn/Spring-Boot/Kafka/?github) 对应 [lab-03](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-03)\n* [《芋道 Spring Cloud 消息队列 Kafka 入门》](http://www.iocoder.cn/Spring-Cloud/Kafka/?github) 对应 [labx-11-spring-cloud-stream-kafka](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-11-spring-cloud-stream-kafka)\n* [《芋道 Spring Cloud 事件总线 Bus Kafka 入门》](http://www.iocoder.cn/Spring-Cloud/Bus-Kafka/?github) 对应 [labx-18](https://github.com/YunaiV/SpringBoot-Labs/tree/master/labx-18)\n* [《Kafka 书单整理》](http://www.iocoder.cn/Books/Kafka-books-recommended/?github)\n\n## ActiveMQ\n\n* [《ActiveMQ 极简入门》](http://www.iocoder.cn/ActiveMQ/install/?github)\n* [《芋道 Spring Boot 消息队列 ActiveMQ 入门》](http://www.iocoder.cn/Spring-Boot/ActiveMQ/?github) 对应 [lab-32](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-32)\n* [《芋道 Spring Cloud 消息队列 ActiveMQ 入门》](http://www.iocoder.cn/Spring-Cloud/ActiveMQ/?github)\n\n# 分布式事务专栏\n\n目前分布式事务的解决方案有 AT、TCC、Saga、MQ、XA、BED 六种。\n\n## AT 方案\n\n* [《芋道 Spring Boot 分布式事务 Seata 入门》](http://www.iocoder.cn/Spring-Boot/Seata/?github)的[「2. AT 模式 + 多数据源」](#)小节，实现单体 Spring Boot 项目在多数据源下的分布式事务\n* [《芋道 Spring Boot 分布式事务 Seata 入门》](http://www.iocoder.cn/Spring-Boot/Seata/?github)的[「AT 模式 + HttpClient 远程调用」](#)小节，实现多个 Spring Boot 项目的分布式事务\n* [《芋道 Dubbo 分布式事务 Seata 入门》](http://www.iocoder.cn/Dubbo/Seata/?github) 的[「2. AT 模式」](#)小节，实现多个 Dubbo 服务的分布式事务。\n* [《芋道 Spring Cloud Alibaba 分布式事务 Seata 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/Seata/?github)的[「3. AT 模式 + Feign」](#)小节，实现多个 Spring Cloud 服务下的分布式事务。\n\n## TCC 方案\n\n* [《TCC-Transaction 源码解析系列》](http://www.iocoder.cn/categories/TCC-Transaction/?github)\n\n## Saga 方案\n\n## MQ 方案\n\n* [《芋道 Spring Boot 消息队列 RocketMQ 入门》](http://www.iocoder.cn/Spring-Boot/RocketMQ/?github) 的[「9. 事务消息」](#)小节\n* [《芋道 Spring Cloud Alibaba 消息队列 RocketMQ 入门》](http://www.iocoder.cn/Spring-Cloud-Alibaba/RocketMQ/?github) 的[「10. 事务消息」](#)小节\n* [《RocketMQ 源码分析 —— 事务消息》](http://www.iocoder.cn/RocketMQ/message-transaction/?github)\n\n## XA 方案\n\n* [《MyCAT 源码分析 —— XA分布式事务》](http://www.iocoder.cn/MyCAT/xa-distributed-transaction/?github)\n\n## BED 方案\n\n* [《Sharding-JDBC 源码分析 —— 分布式事务（一）之最大努力型》](http://www.iocoder.cn/Sharding-JDBC/transaction-bed/?github)\n\n---------\n\n如下是草稿目录，未来需要整理下\n\n# lab-50\n\nEmail 示例\n\n# lab-69-proxy\n\n动态代理\n"
  },
  {
    "path": "httpRequests/2020-12-20T004250.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T004328.500.json",
    "content": "{\n  \"timestamp\": \"2020-12-19T16:43:28.332+0000\",\n  \"status\": 500,\n  \"error\": \"Internal Server Error\",\n  \"message\": \"小朋友，你的账号密码不正确哟！\",\n  \"path\": \"/user/login\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T004337.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T004347.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T004347.500.json",
    "content": "{\n  \"timestamp\": \"2020-12-19T16:43:47.777+0000\",\n  \"status\": 500,\n  \"error\": \"Internal Server Error\",\n  \"message\": \"小朋友，你的账号密码不正确哟！\",\n  \"path\": \"/user/login\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T004358.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T004358.500.json",
    "content": "{\n  \"timestamp\": \"2020-12-19T16:43:58.210+0000\",\n  \"status\": 500,\n  \"error\": \"Internal Server Error\",\n  \"message\": \"小朋友，你的账号密码不正确哟！\",\n  \"path\": \"/user/login\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T004401.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T004401.500.json",
    "content": "{\n  \"timestamp\": \"2020-12-19T16:44:01.275+0000\",\n  \"status\": 500,\n  \"error\": \"Internal Server Error\",\n  \"message\": \"小朋友，你的账号密码不正确哟！\",\n  \"path\": \"/user/login\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T004538.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T004547.500.json",
    "content": "{\n  \"timestamp\": \"2020-12-19T16:45:47.364+0000\",\n  \"status\": 500,\n  \"error\": \"Internal Server Error\",\n  \"message\": \"小朋友，你的账号密码不正确哟！\",\n  \"path\": \"/user/login\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T004638.500.json",
    "content": "{\n  \"timestamp\": \"2020-12-19T16:46:38.505+0000\",\n  \"status\": 500,\n  \"error\": \"Internal Server Error\",\n  \"message\": \"小朋友，你的账号密码不正确哟！\",\n  \"path\": \"/user/login\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T004645.500.json",
    "content": "{\n  \"timestamp\": \"2020-12-19T16:46:45.875+0000\",\n  \"status\": 500,\n  \"error\": \"Internal Server Error\",\n  \"message\": \"小朋友，你的账号密码不正确哟！\",\n  \"path\": \"/user/login\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T004809.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T004813.500.json",
    "content": "{\n  \"timestamp\": \"2020-12-19T16:48:13.674+0000\",\n  \"status\": 500,\n  \"error\": \"Internal Server Error\",\n  \"message\": \"小朋友，你的账号密码不正确哟！\",\n  \"path\": \"/user/login\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T010724.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T010738.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T010809.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T010823.500.json",
    "content": "{\n  \"timestamp\": \"2020-12-19T17:08:23.036+0000\",\n  \"status\": 500,\n  \"error\": \"Internal Server Error\",\n  \"message\": \"小朋友，你的账号密码不正确哟！\",\n  \"path\": \"/user/login\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T010840.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T011020.500.json",
    "content": "{\n  \"timestamp\": \"2020-12-19T17:10:20.615+0000\",\n  \"status\": 500,\n  \"error\": \"Internal Server Error\",\n  \"message\": \"小朋友，你的账号密码不正确哟！\",\n  \"path\": \"/user/login\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T011347.500.json",
    "content": "{\n  \"timestamp\": \"2020-12-19T17:13:47.777+0000\",\n  \"status\": 500,\n  \"error\": \"Internal Server Error\",\n  \"message\": \"小朋友，你没有登录哟！\",\n  \"path\": \"/user/get-current\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T011526.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T011530.200.json",
    "content": "{\n  \"gender\": 1,\n  \"nickname\": \"芋道源码\",\n  \"id\": 1\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T011541.400.json",
    "content": "{\n  \"timestamp\": \"2020-12-19T17:15:40.992+0000\",\n  \"status\": 400,\n  \"error\": \"Bad Request\",\n  \"message\": \"Missing request header 'Authorization' for method parameter of type String\",\n  \"path\": \"/user/get-current\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T011551.500.json",
    "content": "{\n  \"timestamp\": \"2020-12-19T17:15:51.919+0000\",\n  \"status\": 500,\n  \"error\": \"Internal Server Error\",\n  \"message\": \"小朋友，你没有登录哟！\",\n  \"path\": \"/user/get-current\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T011556.200.json",
    "content": "{\n  \"gender\": 1,\n  \"nickname\": \"芋道源码\",\n  \"id\": 1\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T011628-1.200.json",
    "content": "{\n  \"gender\": 1,\n  \"nickname\": \"芋道源码\",\n  \"id\": 1\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T011628.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T011646.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T011650.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T011653.500.json",
    "content": "{\n  \"timestamp\": \"2020-12-19T17:16:53.489+0000\",\n  \"status\": 500,\n  \"error\": \"Internal Server Error\",\n  \"message\": \"小朋友，你没有登录哟！\",\n  \"path\": \"/user/get-current\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T011818.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T011843.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T011847.500.json",
    "content": "{\n  \"timestamp\": \"2020-12-19T17:18:47.862+0000\",\n  \"status\": 500,\n  \"error\": \"Internal Server Error\",\n  \"message\": \"小朋友，你没有登录哟！\",\n  \"path\": \"/user/get-current\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T012507.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T012510.200.json",
    "content": "{\n  \"gender\": 1,\n  \"nickname\": \"芋道源码\",\n  \"id\": 1\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T012527.200.json",
    "content": "{\n  \"gender\": 1,\n  \"nickname\": \"芋道源码\",\n  \"id\": 1\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T012540.400.json",
    "content": "{\n  \"timestamp\": \"2020-12-19T17:25:40.604+0000\",\n  \"status\": 400,\n  \"error\": \"Bad Request\",\n  \"message\": \"Missing request header 'Authorization' for method parameter of type String\",\n  \"path\": \"/user/get-current\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T012544.200.json",
    "content": "{\n  \"gender\": 1,\n  \"nickname\": \"芋道源码\",\n  \"id\": 1\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T012703.500.json",
    "content": "{\n  \"timestamp\": \"2020-12-19T17:27:03.272+0000\",\n  \"status\": 500,\n  \"error\": \"Internal Server Error\",\n  \"message\": \"小朋友，你没有登录哟！\",\n  \"path\": \"/user/get-current\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T012708.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T012710.200.json",
    "content": "{\n  \"gender\": 1,\n  \"nickname\": \"芋道源码\",\n  \"id\": 1\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T013544.200.json",
    "content": "{\n  \"gender\": 1,\n  \"nickname\": \"芋道源码\",\n  \"id\": 1\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T013552.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T013558.500.json",
    "content": "{\n  \"timestamp\": \"2020-12-19T17:35:58.033+0000\",\n  \"status\": 500,\n  \"error\": \"Internal Server Error\",\n  \"message\": \"小朋友，你没有登录哟！\",\n  \"path\": \"/user/get-current\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T013845.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T014019.200.json",
    "content": "{\n  \"gender\": 1,\n  \"nickname\": \"芋道源码\",\n  \"id\": 1\n}\n"
  },
  {
    "path": "httpRequests/2020-12-20T021415.200.json",
    "content": "{\n  \"userId\": 1,\n  \"token\": \"token001\"\n}\n"
  },
  {
    "path": "httpRequests/http-client.cookies",
    "content": "# domain\tpath\tname\tvalue\tdate\n"
  },
  {
    "path": "httpRequests/http-requests-log.http",
    "content": "POST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T021415.200.json\n\n###\n\nGET http://127.0.0.1:8080/user/get-current?full=true\nAuthorization: token001\n\n<> 2020-12-20T014019.200.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T013845.200.json\n\n###\n\nGET http://127.0.0.1:8080/user/get-current?full=true\nAuthorization: 1\n\n<> 2020-12-20T013558.500.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T013552.200.json\n\n###\n\nGET http://127.0.0.1:8080/user/get-current?full=true\nAuthorization: token001\n\n<> 2020-12-20T013544.200.json\n\n###\n\nGET http://127.0.0.1:8080/user/get-current?full=true\nAuthorization: token001\n\n<> 2020-12-20T012710.200.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T012708.200.json\n\n###\n\nGET http://127.0.0.1:8080/user/get-current?full=true\nAuthorization: {{token_from_server}}\n\n<> 2020-12-20T012703.500.json\n\n###\n\nGET http://127.0.0.1:8080/user/get-current?full=true\nAuthorization: {{token_from_server}}\n\n###\n\nGET http://127.0.0.1:8080/user/get-current?full=true\nAuthorization: token001\n\n<> 2020-12-20T012544.200.json\n\n###\n\nGET http://127.0.0.1:8080/user/get-current?full=true\n\n<> 2020-12-20T012540.400.json\n\n###\n\nGET http://127.0.0.1:8080/user/get-current?full=true\nAuthorization: token001\n\n<> 2020-12-20T012527.200.json\n\n###\n\nGET http://127.0.0.1:8080/user/get-current?full=true\nAuthorization: token001\n\n<> 2020-12-20T012510.200.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T012507.200.json\n\n###\n\nGET http://127.0.0.1:8080/user/get-current?full=true\nAuthorization: 1\n\n<> 2020-12-20T011847.500.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T011843.200.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T011818.200.json\n\n###\n\nGET http://127.0.0.1:8080/user/get-current?full=true\nAuthorization: 0\n\n<> 2020-12-20T011653.500.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T011650.200.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T011646.200.json\n\n###\n\nGET http://127.0.0.1:8080/user/get-current?full=true\nAuthorization: token001\n\n<> 2020-12-20T011628-1.200.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T011628.200.json\n\n###\n\nGET http://127.0.0.1:8080/user/get-current?full=true\nAuthorization: token001\n\n<> 2020-12-20T011556.200.json\n\n###\n\nGET http://127.0.0.1:8080/user/get-current?full=true\nAuthorization: {{token_from_server1}}\n\n<> 2020-12-20T011551.500.json\n\n###\n\nGET http://127.0.0.1:8080/user/get-current?full=true\n\n<> 2020-12-20T011541.400.json\n\n###\n\nGET http://127.0.0.1:8080/user/get-current?full=true\nAuthorization: token001\n\n<> 2020-12-20T011530.200.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T011526.200.json\n\n###\n\nGET http://127.0.0.1:8080/user/get-current?full=true\nAuthorization: token002\n\n<> 2020-12-20T011347.500.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=buzhidao\n\n<> 2020-12-20T011020.500.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T010840.200.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=buzhidao\n\n<> 2020-12-20T010823.500.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T010809.200.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T010738.200.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T010724.200.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=buzhidao\n\n<> 2020-12-20T004813.500.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T004809.200.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=buzhidao\n\n<> 2020-12-20T004645.500.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=buzhidao\n\n<> 2020-12-20T004638.500.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=buzhidao\n\n<> 2020-12-20T004547.500.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T004538.200.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=buzhidao\n\n<> 2020-12-20T004401.500.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T004401.200.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=buzhidao\n\n<> 2020-12-20T004358.500.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T004358.200.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=buzhidao\n\n<> 2020-12-20T004347.500.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T004347.200.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T004337.200.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=buzhidao\n\n<> 2020-12-20T004328.500.json\n\n###\n\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n<> 2020-12-20T004250.200.json\n\n###\n\n"
  },
  {
    "path": "lab-01-spring-security/lab-01-springsecurity-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.10.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-01-springsecurity-demo</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-01-spring-security/lab-01-springsecurity-demo/src/main/java/cn/iocoder/springboot/lab01/springsecurity/Application.java",
    "content": "package cn.iocoder.springboot.lab01.springsecurity;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-01-spring-security/lab-01-springsecurity-demo/src/main/java/cn/iocoder/springboot/lab01/springsecurity/controller/AdminController.java",
    "content": "package cn.iocoder.springboot.lab01.springsecurity.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/admin\")\npublic class AdminController {\n\n    @GetMapping(\"/demo\")\n    public String demo() {\n        return \"示例返回\";\n    }\n\n}\n"
  },
  {
    "path": "lab-01-spring-security/lab-01-springsecurity-demo/src/main/resources/application.yaml",
    "content": "spring:\n  # Spring Security 配置项，对应 SecurityProperties 配置类\n  security:\n    # 配置默认的 InMemoryUserDetailsManager 的用户账号与密码。\n    user:\n      name: user # 账号\n      password: user # 密码\n      roles: ADMIN # 拥有角色\n"
  },
  {
    "path": "lab-01-spring-security/lab-01-springsecurity-demo-role/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.10.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-01-springsecurity-demo-role</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-01-spring-security/lab-01-springsecurity-demo-role/src/main/java/cn/iocoder/springboot/lab01/springsecurity/Application.java",
    "content": "package cn.iocoder.springboot.lab01.springsecurity;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-01-spring-security/lab-01-springsecurity-demo-role/src/main/java/cn/iocoder/springboot/lab01/springsecurity/config/SecurityConfig.java",
    "content": "package cn.iocoder.springboot.lab01.springsecurity.config;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\n\n@Configuration\n@EnableGlobalMethodSecurity(prePostEnabled = true)\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n        http\n                // 配置请求地址的权限\n                .authorizeRequests()\n                    .antMatchers(\"/test/demo\").permitAll() // 所有用户可访问\n                    .antMatchers(\"/test/admin\").hasRole(\"ADMIN\") // 需要 ADMIN 角色\n                    .antMatchers(\"/test/normal\").access(\"hasRole('ROLE_NORMAL')\") // 需要 NORMAL 角色。\n                    // 任何请求，访问的用户都需要经过认证\n                    .anyRequest().authenticated()\n                .and()\n                // 设置 Form 表单登陆\n                .formLogin()\n//                    .loginPage(\"/login\") // 登陆 URL 地址\n                    .permitAll() // 所有用户可访问\n                .and()\n                // 配置退出相关\n                .logout()\n//                    .logoutUrl(\"/logout\") // 退出 URL 地址\n                    .permitAll(); // 所有用户可访问\n    }\n\n    @Override\n    protected void configure(AuthenticationManagerBuilder auth) throws Exception {\n        auth.\n                // 使用内存中的 InMemoryUserDetailsManager\n                inMemoryAuthentication()\n                // 不使用 PasswordEncoder 密码编码器\n                .passwordEncoder(NoOpPasswordEncoder.getInstance())\n                // 配置 admin 用户\n                .withUser(\"admin\").password(\"admin\").roles(\"ADMIN\")\n                // 配置 normal 用户\n                .and().withUser(\"normal\").password(\"normal\").roles(\"NORMAL\");\n    }\n\n}\n"
  },
  {
    "path": "lab-01-spring-security/lab-01-springsecurity-demo-role/src/main/java/cn/iocoder/springboot/lab01/springsecurity/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab01.springsecurity.controller;\n\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.annotation.security.PermitAll;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @PermitAll\n    @GetMapping(\"/echo\")\n    public String demo() {\n        return \"示例返回\";\n    }\n\n    @GetMapping(\"/home\")\n    public String home() {\n        return \"我是首页\";\n    }\n\n    @PreAuthorize(\"hasRole('ROLE_ADMIN')\")\n    @GetMapping(\"/admin\")\n    public String admin() {\n        return \"我是管理员\";\n    }\n\n    @PreAuthorize(\"hasRole('ROLE_NORMAL')\")\n    @GetMapping(\"/normal\")\n    public String normal() {\n        return \"我是普通用户\";\n    }\n\n}\n"
  },
  {
    "path": "lab-01-spring-security/lab-01-springsecurity-demo-role/src/main/java/cn/iocoder/springboot/lab01/springsecurity/controller/TestController.java",
    "content": "package cn.iocoder.springboot.lab01.springsecurity.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/test\")\npublic class TestController {\n\n    @GetMapping(\"/demo\")\n    public String demo() {\n        return \"示例返回\";\n    }\n\n    @GetMapping(\"/home\")\n    public String home() {\n        return \"我是首页\";\n    }\n\n    @GetMapping(\"/admin\")\n    public String admin() {\n        return \"我是管理员\";\n    }\n\n    @GetMapping(\"/normal\")\n    public String normal() {\n        return \"我是普通用户\";\n    }\n\n}\n"
  },
  {
    "path": "lab-01-spring-security/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-02-spring-security</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-01-springsecurity-demo</module>\n        <module>lab-01-springsecurity-demo-role</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-01-spring-security/《芋道 Spring Boot 安全框架 Spring Security 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Spring-Security/?github>\n"
  },
  {
    "path": "lab-02-spring-security-oauth/authorization-code-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.16.RELEASE</version>\n        <relativePath /> <!-- lookup parent from repository -->\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>authorization-code-server</artifactId>\n\n    <dependencies>\n        <!-- for Spring MVC -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- for Spring Security -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- for OAuth 2.0 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth</groupId>\n            <artifactId>spring-security-oauth2</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>"
  },
  {
    "path": "lab-02-spring-security-oauth/authorization-code-server/src/main/java/cn/iocoder/springboot/labs/lab01/Application.java",
    "content": "package cn.iocoder.springboot.labs.lab01;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/authorization-code-server/src/main/java/cn/iocoder/springboot/labs/lab01/authorization/OAuth2AuthorizationServer.java",
    "content": "package cn.iocoder.springboot.labs.lab01.authorization;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\n\n// 授权服务器配置\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"clientapp\").secret(\"112233\") // Client 账号、密码。\n                .redirectUris(\"http://localhost:9001/callback\") // 配置回调地址，选填。\n                .authorizedGrantTypes(\"authorization_code\") // 授权码模式\n                .scopes(\"read_userinfo\", \"read_contacts\") // 可授权的 Scope\n//                .and().withClient() // 可以继续配置新的 Client\n                ;\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/authorization-code-server/src/main/java/cn/iocoder/springboot/labs/lab01/resource/OAuth2ResourceServer.java",
    "content": "package cn.iocoder.springboot.labs.lab01.resource;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;\n\n// 资源服务配置\n@Configuration\n@EnableResourceServer\npublic class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n        http.authorizeRequests()\n                // 对 \"/api/**\" 开启认证\n                .anyRequest()\n                .authenticated()\n                .and()\n                .requestMatchers()\n                .antMatchers(\"/api/**\");\n    }\n\n}\n\n// 实际，OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。\n// 主要考虑，简化 demo ，所以改成这样。"
  },
  {
    "path": "lab-02-spring-security-oauth/authorization-code-server/src/main/java/cn/iocoder/springboot/labs/lab01/resource/api/ExampleController.java",
    "content": "package cn.iocoder.springboot.labs.lab01.resource.api;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 示例模块 Controller\n */\n@RestController\n@RequestMapping(\"/api/example\")\npublic class ExampleController {\n\n    @RequestMapping(\"/hello\")\n    public String hello() {\n        return \"world\";\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/authorization-code-server/src/main/resources/application.properties",
    "content": "# Spring Security Setting\nsecurity.user.name=yunai\nsecurity.user.password=1024"
  },
  {
    "path": "lab-02-spring-security-oauth/client-credentials-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.16.RELEASE</version>\n        <relativePath /> <!-- lookup parent from repository -->\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>client-credentials-server</artifactId>\n\n    <dependencies>\n        <!-- for Spring MVC -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- for Spring Security -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- for OAuth 2.0 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth</groupId>\n            <artifactId>spring-security-oauth2</artifactId>\n        </dependency>\n\n    </dependencies>\n\n\n</project>"
  },
  {
    "path": "lab-02-spring-security-oauth/client-credentials-server/src/main/java/lab01/Application.java",
    "content": "package lab01;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/client-credentials-server/src/main/java/lab01/authorization/OAuth2AuthorizationServer.java",
    "content": "package lab01.authorization;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\n\n// 授权服务器配置\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"clientapp\").secret(\"112233\") // Client 账号、密码。\n                .authorizedGrantTypes(\"client_credentials\") // 授权码模式\n                .scopes(\"read_userinfo\", \"read_contacts\") // 可授权的 Scope\n//                .and().withClient() // 可以继续配置新的 Client\n                ;\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/client-credentials-server/src/main/java/lab01/resource/OAuth2ResourceServer.java",
    "content": "package lab01.resource;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;\n\n// 资源服务配置\n@Configuration\n@EnableResourceServer\npublic class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n        http.authorizeRequests()\n                // 对 \"/api/**\" 开启认证\n                .anyRequest()\n                .authenticated()\n                .and()\n                .requestMatchers()\n                .antMatchers(\"/api/**\");\n    }\n\n}\n\n// 实际，OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。\n// 主要考虑，简化 demo ，所以改成这样。"
  },
  {
    "path": "lab-02-spring-security-oauth/client-credentials-server/src/main/java/lab01/resource/api/ExampleController.java",
    "content": "package lab01.resource.api;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 示例模块 Controller\n */\n@RestController\n@RequestMapping(\"/api/example\")\npublic class ExampleController {\n\n    @RequestMapping(\"/hello\")\n    public String hello() {\n        return \"world\";\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/implicit-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.16.RELEASE</version>\n        <relativePath /> <!-- lookup parent from repository -->\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>implicit-server</artifactId>\n\n    <dependencies>\n        <!-- for Spring MVC -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- for Spring Security -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- for OAuth 2.0 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth</groupId>\n            <artifactId>spring-security-oauth2</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>"
  },
  {
    "path": "lab-02-spring-security-oauth/implicit-server/src/main/java/lab01/Application.java",
    "content": "package lab01;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/implicit-server/src/main/java/lab01/authorization/OAuth2AuthorizationServer.java",
    "content": "package lab01.authorization;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\n\n// 授权服务器配置\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"clientapp\").secret(\"112233\") // Client 账号、密码。\n                .redirectUris(\"http://localhost:9001/callback\") // 配置回调地址，选填。\n                .authorizedGrantTypes(\"implicit\") // 授权码模式\n                .scopes(\"read_userinfo\", \"read_contacts\") // 可授权的 Scope\n//                .and().withClient() // 可以继续配置新的 Client\n                ;\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/implicit-server/src/main/java/lab01/resource/OAuth2ResourceServer.java",
    "content": "package lab01.resource;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;\n\n// 资源服务配置\n@Configuration\n@EnableResourceServer\npublic class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n        http.authorizeRequests()\n                // 对 \"/api/**\" 开启认证\n                .anyRequest()\n                .authenticated()\n                .and()\n                .requestMatchers()\n                .antMatchers(\"/api/**\")\n//                .and()\n//                .cors()\n        ;\n    }\n\n}\n\n// 实际，OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。\n// 主要考虑，简化 demo ，所以改成这样。"
  },
  {
    "path": "lab-02-spring-security-oauth/implicit-server/src/main/java/lab01/resource/api/ExampleController.java",
    "content": "package lab01.resource.api;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 示例模块 Controller\n */\n@RestController\n@RequestMapping(\"/api/example\")\npublic class ExampleController {\n\n//    @CrossOrigin\n    @RequestMapping(\"/hello\")\n    public String hello() {\n        return \"world\";\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/implicit-server/src/main/resources/application.properties",
    "content": "# Spring Security Setting\nsecurity.user.name=yunai\nsecurity.user.password=1024"
  },
  {
    "path": "lab-02-spring-security-oauth/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-02-spring-security-oauth</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>authorization-code-server</module>\n        <module>resource-owner-password-credentials-server</module>\n        <module>implicit-server</module>\n        <module>client-credentials-server</module>\n        <module>resource-owner-password-credentials-server-with-refresh-token</module>\n        <module>resource-owner-password-credentials-server-with-revoke-token</module>\n        <module>resource-owner-password-credentials-server-by-jdbc-token-store</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.16.RELEASE</version>\n        <relativePath /> <!-- lookup parent from repository -->\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>resource-owner-password-credentials-server</artifactId>\n\n    <dependencies>\n        <!-- for Spring MVC -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- for Spring Security -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- for OAuth 2.0 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth</groupId>\n            <artifactId>spring-security-oauth2</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server/src/main/java/lab01/Application.java",
    "content": "package lab01;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server/src/main/java/lab01/authorization/OAuth2AuthorizationServer.java",
    "content": "package lab01.authorization;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;\n\n// 授权服务器配置\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {\n\n    // 用户认证\n    @Autowired\n    private AuthenticationManager authenticationManager;\n\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n        endpoints.authenticationManager(authenticationManager);\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"clientapp\").secret(\"112233\") // Client 账号、密码。\n                .authorizedGrantTypes(\"password\") // 密码模式\n                .scopes(\"read_userinfo\", \"read_contacts\") // 可授权的 Scope\n//                .and().withClient() // 可以继续配置新的 Client\n                ;\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server/src/main/java/lab01/resource/OAuth2ResourceServer.java",
    "content": "package lab01.resource;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;\n\n// 资源服务配置\n@Configuration\n@EnableResourceServer\npublic class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n        http.authorizeRequests()\n                // 对 \"/api/**\" 开启认证\n                .anyRequest()\n                .authenticated()\n                .and()\n                .requestMatchers()\n                .antMatchers(\"/api/**\");\n    }\n\n}\n\n// 实际，OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。\n// 主要考虑，简化 demo ，所以改成这样。"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server/src/main/java/lab01/resource/api/ExampleController.java",
    "content": "package lab01.resource.api;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 示例模块 Controller\n */\n@RestController\n@RequestMapping(\"/api/example\")\npublic class ExampleController {\n\n    @RequestMapping(\"/hello\")\n    public String hello() {\n        return \"world\";\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server/src/main/resources/application.properties",
    "content": "# Spring Security Setting\nsecurity.user.name=yunai\nsecurity.user.password=1024"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-by-jdbc-token-store/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.16.RELEASE</version>\n        <relativePath /> <!-- lookup parent from repository -->\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>resource-owner-password-credentials-server-by-jdbc-token-store</artifactId>\n\n    <dependencies>\n        <!-- for Spring MVC -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- for Spring Security -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- for OAuth 2.0 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth</groupId>\n            <artifactId>spring-security-oauth2</artifactId>\n        </dependency>\n\n        <!-- jdbc token store -->\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-jdbc</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <scope>runtime</scope>\n        </dependency>\n\n\n    </dependencies>\n\n</project>"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-by-jdbc-token-store/src/main/java/lab02/Application.java",
    "content": "package lab02;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-by-jdbc-token-store/src/main/java/lab02/authorization/OAuth2AuthorizationServer.java",
    "content": "package lab02.authorization;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.env.Environment;\nimport org.springframework.jdbc.datasource.DriverManagerDataSource;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;\nimport org.springframework.security.oauth2.provider.token.TokenStore;\nimport org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;\n\nimport javax.sql.DataSource;\n\n// 授权服务器配置\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {\n\n    @Autowired\n    private Environment env;\n\n    // 用户认证\n    @Autowired\n    private AuthenticationManager authenticationManager;\n\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n        endpoints.tokenStore(tokenStore()) // 设置 tokenStore\n                .authenticationManager(authenticationManager); // 设置 authenticationManager\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n//        clients.inMemory()\n//                .withClient(\"clientapp\").secret(\"112233\") // Client 账号、密码。\n//                .authorizedGrantTypes(\"password\") // 密码模式\n//                .scopes(\"read_userinfo\", \"read_contacts\") // 可授权的 Scope\n////                .and().withClient() // 可以继续配置新的 Client\n//                ;\n\n        // 加载 ClientDetails\n        clients.jdbc(dataSource());\n    }\n\n    @Bean\n    public DataSource dataSource() {\n        final DriverManagerDataSource dataSource = new DriverManagerDataSource();\n        dataSource.setDriverClassName(env.getProperty(\"jdbc.driverClassName\"));\n        dataSource.setUrl(env.getProperty(\"jdbc.url\"));\n        dataSource.setUsername(env.getProperty(\"jdbc.user\"));\n        dataSource.setPassword(env.getProperty(\"jdbc.pass\"));\n        return dataSource;\n    }\n\n    @Bean\n    public TokenStore tokenStore() {\n        return new JdbcTokenStore(dataSource());\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-by-jdbc-token-store/src/main/java/lab02/resource/OAuth2ResourceServer.java",
    "content": "package lab02.resource;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;\n\n// 资源服务配置\n@Configuration\n@EnableResourceServer\npublic class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n        http.authorizeRequests()\n                // 对 \"/api/**\" 开启认证\n                .anyRequest()\n                .authenticated()\n                .and()\n                .requestMatchers()\n                .antMatchers(\"/api/**\");\n    }\n\n}\n\n// 实际，OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。\n// 主要考虑，简化 demo ，所以改成这样。"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-by-jdbc-token-store/src/main/java/lab02/resource/api/ExampleController.java",
    "content": "package lab02.resource.api;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 示例模块 Controller\n */\n@RestController\n@RequestMapping(\"/api/example\")\npublic class ExampleController {\n\n    @RequestMapping(\"/hello\")\n    public String hello() {\n        return \"world\";\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-by-jdbc-token-store/src/main/resources/application.properties",
    "content": "# Spring Security Setting\nsecurity.user.name=yunai\nsecurity.user.password=1024\n\n##################### MySQL #####################\njdbc.driverClassName=com.mysql.jdbc.Driver\njdbc.url=jdbc:mysql://127.0.0.1:33061/oauth2\njdbc.user=root\njdbc.pass=123456"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-by-jdbc-token-store/src/main/resources/data.sql",
    "content": "INSERT INTO oauth_client_details\n\t(client_id, client_secret, scope, authorized_grant_types,\n\tweb_server_redirect_uri, authorities, access_token_validity,\n\trefresh_token_validity, additional_information, autoapprove)\nVALUES\n\t('fooClientIdPassword', 'secret', 'foo,read,write',\n\t'password,authorization_code,refresh_token', null, null, 36000, 36000, null, true);\nINSERT INTO oauth_client_details\n\t(client_id, client_secret, scope, authorized_grant_types,\n\tweb_server_redirect_uri, authorities, access_token_validity,\n\trefresh_token_validity, additional_information, autoapprove)\nVALUES\n\t('sampleClientId', 'secret', 'read,write,foo,bar',\n\t'implicit', null, null, 36000, 36000, null, false);\nINSERT INTO oauth_client_details\n\t(client_id, client_secret, scope, authorized_grant_types,\n\tweb_server_redirect_uri, authorities, access_token_validity,\n\trefresh_token_validity, additional_information, autoapprove)\nVALUES\n\t('barClientIdPassword', 'secret', 'bar,read,write',\n\t'password,authorization_code,refresh_token', null, null, 36000, 36000, null, true);"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-by-jdbc-token-store/src/main/resources/schema.sql",
    "content": "--------------- MySQL ---------------\ndrop table if exists oauth_client_details;\ncreate table oauth_client_details (\n  client_id VARCHAR(255) PRIMARY KEY,\n  resource_ids VARCHAR(255),\n  client_secret VARCHAR(255),\n  scope VARCHAR(255),\n  authorized_grant_types VARCHAR(255),\n  web_server_redirect_uri VARCHAR(255),\n  authorities VARCHAR(255),\n  access_token_validity INTEGER,\n  refresh_token_validity INTEGER,\n  additional_information VARCHAR(4096),\n  autoapprove VARCHAR(255)\n);\n\ncreate table if not exists oauth_client_token (\n  token_id VARCHAR(255),\n  token LONG VARBINARY,\n  authentication_id VARCHAR(255) PRIMARY KEY,\n  user_name VARCHAR(255),\n  client_id VARCHAR(255)\n);\n\ncreate table if not exists oauth_access_token (\n  token_id VARCHAR(255),\n  token LONG VARBINARY,\n  authentication_id VARCHAR(255) PRIMARY KEY,\n  user_name VARCHAR(255),\n  client_id VARCHAR(255),\n  authentication LONG VARBINARY,\n  refresh_token VARCHAR(255)\n);\n\ncreate table if not exists oauth_refresh_token (\n  token_id VARCHAR(255),\n  token LONG VARBINARY,\n  authentication LONG VARBINARY\n);\n\ncreate table if not exists oauth_code (\n  code VARCHAR(255), authentication LONG VARBINARY\n);\n\ncreate table if not exists oauth_approvals (\n\tuserId VARCHAR(255),\n\tclientId VARCHAR(255),\n\tscope VARCHAR(255),\n\tstatus VARCHAR(10),\n\texpiresAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n\tlastModifiedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\ncreate table if not exists ClientDetails (\n  appId VARCHAR(255) PRIMARY KEY,\n  resourceIds VARCHAR(255),\n  appSecret VARCHAR(255),\n  scope VARCHAR(255),\n  grantTypes VARCHAR(255),\n  redirectUrl VARCHAR(255),\n  authorities VARCHAR(255),\n  access_token_validity INTEGER,\n  refresh_token_validity INTEGER,\n  additionalInformation VARCHAR(4096),\n  autoApproveScopes VARCHAR(255)\n);"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-refresh-token/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.16.RELEASE</version>\n        <relativePath /> <!-- lookup parent from repository -->\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>resource-owner-password-credentials-server-with-refresh-token</artifactId>\n\n    <dependencies>\n        <!-- for Spring MVC -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- for Spring Security -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- for OAuth 2.0 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth</groupId>\n            <artifactId>spring-security-oauth2</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-refresh-token/src/main/java/lab01/Application.java",
    "content": "package lab01;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-refresh-token/src/main/java/lab01/authorization/OAuth2AuthorizationServer.java",
    "content": "package lab01.authorization;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;\n\n// 授权服务器配置\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {\n\n    // 用户认证\n    @Autowired\n    private AuthenticationManager authenticationManager;\n\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n        endpoints.authenticationManager(authenticationManager);\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"clientapp\").secret(\"112233\") // Client 账号、密码。\n                .authorizedGrantTypes(\"password\", \"refresh_token\") // 密码模式\n                .scopes(\"read_userinfo\", \"read_contacts\") // 可授权的 Scope\n                .refreshTokenValiditySeconds(1200) // 1200 秒过期\n//                .and().withClient() // 可以继续配置新的 Client\n                ;\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-refresh-token/src/main/java/lab01/resource/OAuth2ResourceServer.java",
    "content": "package lab01.resource;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;\n\n// 资源服务配置\n@Configuration\n@EnableResourceServer\npublic class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n        http.authorizeRequests()\n                // 对 \"/api/**\" 开启认证\n                .anyRequest()\n                .authenticated()\n                .and()\n                .requestMatchers()\n                .antMatchers(\"/api/**\");\n    }\n\n}\n\n// 实际，OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。\n// 主要考虑，简化 demo ，所以改成这样。"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-refresh-token/src/main/java/lab01/resource/api/ExampleController.java",
    "content": "package lab01.resource.api;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 示例模块 Controller\n */\n@RestController\n@RequestMapping(\"/api/example\")\npublic class ExampleController {\n\n    @RequestMapping(\"/hello\")\n    public String hello() {\n        return \"world\";\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-refresh-token/src/main/resources/application.properties",
    "content": "# Spring Security Setting\nsecurity.user.name=yunai\nsecurity.user.password=1024"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-revoke-token/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.16.RELEASE</version>\n        <relativePath /> <!-- lookup parent from repository -->\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n    <artifactId>resource-owner-password-credentials-server-with-revoke-token</artifactId>\n\n    <dependencies>\n        <!-- for Spring MVC -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- for Spring Security -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n        <!-- for OAuth 2.0 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth</groupId>\n            <artifactId>spring-security-oauth2</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-revoke-token/src/main/java/lab2/Application.java",
    "content": "package lab2;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-revoke-token/src/main/java/lab2/authorization/OAuth2AuthorizationServer.java",
    "content": "package lab2.authorization;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;\n\n// 授权服务器配置\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {\n\n    // 用户认证\n    @Autowired\n    private AuthenticationManager authenticationManager;\n\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n        endpoints.authenticationManager(authenticationManager);\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"clientapp\").secret(\"112233\") // Client 账号、密码。\n                .authorizedGrantTypes(\"password\", \"refresh_token\") // 密码模式\n                .scopes(\"read_userinfo\", \"read_contacts\") // 可授权的 Scope\n                .refreshTokenValiditySeconds(1200) // 1200 秒过期\n//                .and().withClient() // 可以继续配置新的 Client\n                ;\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-revoke-token/src/main/java/lab2/authorization/token/TokenController.java",
    "content": "package lab2.authorization.token;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.oauth2.common.DefaultOAuth2RefreshToken;\nimport org.springframework.security.oauth2.provider.token.ConsumerTokenServices;\nimport org.springframework.security.oauth2.provider.token.TokenStore;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestMethod;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class TokenController {\n\n    @Autowired\n    private ConsumerTokenServices tokenServices;\n    @Autowired(required = false)\n    private TokenStore tokenStore;\n\n    @RequestMapping(method = RequestMethod.POST, value = \"api/access_token/revoke\")\n    public String revokeToken(@RequestParam(\"token\") String token) {\n        tokenServices.revokeToken(token);\n        return token;\n    }\n\n    @RequestMapping(method = RequestMethod.POST, value = \"api/refresh_token/revoke\")\n    public String revokeRefreshToken(@RequestParam(\"token\") String token) {\n        tokenStore.removeRefreshToken(new DefaultOAuth2RefreshToken(token));\n        return token;\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-revoke-token/src/main/java/lab2/resource/OAuth2ResourceServer.java",
    "content": "package lab2.resource;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;\n\n// 资源服务配置\n@Configuration\n@EnableResourceServer\npublic class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n        http.authorizeRequests()\n                // 对 \"/api/**\" 开启认证\n                .anyRequest()\n                .authenticated()\n                .and()\n                .requestMatchers()\n                .antMatchers(\"/api/**\");\n    }\n\n}\n\n// 实际，OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。\n// 主要考虑，简化 demo ，所以改成这样。"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-revoke-token/src/main/java/lab2/resource/api/ExampleController.java",
    "content": "package lab2.resource.api;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 示例模块 Controller\n */\n@RestController\n@RequestMapping(\"/api/example\")\npublic class ExampleController {\n\n    @RequestMapping(\"/hello\")\n    public String hello() {\n        return \"world\";\n    }\n\n}"
  },
  {
    "path": "lab-02-spring-security-oauth/resource-owner-password-credentials-server-with-revoke-token/src/main/resources/application.properties",
    "content": "# Spring Security Setting\nsecurity.user.name=yunai\nsecurity.user.password=1024"
  },
  {
    "path": "lab-02-spring-security-oauth/《芋道 Spring Security OAuth2 入门（新）》.md",
    "content": "<http://www.iocoder.cn/Spring-Security/OAuth2-learning/?github>\n\n对应 [lab-68](https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-68) 示例。\n"
  },
  {
    "path": "lab-02-spring-security-oauth/《芋道 Spring Security OAuth2 入门（老）》.md",
    "content": "<http://www.iocoder.cn/Spring-Security/OAuth2-learning-old/?github>\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-03-kafka-demo</artifactId>\n\n    <dependencies>\n        <!-- 引入 Spring-Kafka 依赖 -->\n        <!-- 已经内置 kafka-clients 依赖，所以无需重复引入 -->\n        <dependency>\n            <groupId>org.springframework.kafka</groupId>\n            <artifactId>spring-kafka</artifactId>\n            <version>2.3.3.RELEASE</version>\n        </dependency>\n\n        <!-- 实现对 JSON 的自动化配置 -->\n        <!-- 因为，Kafka 对复杂对象的 Message 序列化时，我们会使用到 JSON -->\n        <!--\n            同时，spring-boot-starter-json 引入了 spring-boot-starter ，而 spring-boot-starter 又引入了 spring-boot-autoconfigure 。\n            spring-boot-autoconfigure 实现了 Spring-Kafka 的自动化配置\n         -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-json</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo/src/main/java/cn/iocoder/springboot/lab03/kafkademo/Application.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo/src/main/java/cn/iocoder/springboot/lab03/kafkademo/config/KafkaConfiguration.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.kafka.core.KafkaTemplate;\nimport org.springframework.kafka.listener.*;\nimport org.springframework.util.backoff.BackOff;\nimport org.springframework.util.backoff.FixedBackOff;\n\n@Configuration\npublic class KafkaConfiguration {\n\n    @Bean\n    @Primary\n    public ErrorHandler kafkaErrorHandler(KafkaTemplate<?, ?> template) {\n        // 创建 DeadLetterPublishingRecoverer 对象\n        ConsumerRecordRecoverer recoverer = new DeadLetterPublishingRecoverer(template);\n        // 创建 FixedBackOff 对象\n        BackOff backOff = new FixedBackOff(10 * 1000L, 3L);\n        // 创建 SeekToCurrentErrorHandler 对象\n        return new SeekToCurrentErrorHandler(recoverer, backOff);\n    }\n\n//    @Bean\n//    @Primary\n//    public BatchErrorHandler kafkaBatchErrorHandler() {\n//        // 创建 SeekToCurrentBatchErrorHandler 对象\n//        SeekToCurrentBatchErrorHandler batchErrorHandler = new SeekToCurrentBatchErrorHandler();\n//        // 创建 FixedBackOff 对象\n//        BackOff backOff = new FixedBackOff(10 * 1000L, 3L);\n//        batchErrorHandler.setBackOff(backOff);\n//        // 返回\n//        return batchErrorHandler;\n//    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo/src/main/java/cn/iocoder/springboot/lab03/kafkademo/consumer/Demo01AConsumer.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.consumer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.message.Demo01Message;\nimport org.apache.kafka.clients.consumer.ConsumerRecord;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.kafka.annotation.KafkaListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01AConsumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @KafkaListener(topics = Demo01Message.TOPIC,\n            groupId = \"demo01-A-consumer-group-\" + Demo01Message.TOPIC)\n    public void onMessage(ConsumerRecord<Integer, String> record) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), record);\n    }\n\n//    @KafkaListener(topics = Demo01Message.TOPIC,\n//            groupId = \"demo01-B-consumer-group-\" + Demo01Message.TOPIC)\n//    public void onMessage(ConsumerRecord<Integer, String> record) throws InterruptedException {\n//        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), record.partition());\n//        Thread.sleep(10 * 1000L);\n//        Thread.sleep(1L);\n//        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), record.partition());\n//    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo/src/main/java/cn/iocoder/springboot/lab03/kafkademo/consumer/Demo01Consumer.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.consumer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.kafka.annotation.KafkaListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @KafkaListener(topics = Demo01Message.TOPIC,\n            groupId = \"demo01-consumer-group-\" + Demo01Message.TOPIC)\n    public void onMessage(Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo/src/main/java/cn/iocoder/springboot/lab03/kafkademo/consumer/Demo04Consumer.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.consumer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.message.Demo04Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.kafka.annotation.KafkaListener;\nimport org.springframework.stereotype.Component;\n\nimport java.util.concurrent.atomic.AtomicInteger;\n\n@Component\npublic class Demo04Consumer {\n\n    private AtomicInteger count = new AtomicInteger(0);\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @KafkaListener(topics = Demo04Message.TOPIC,\n            groupId = \"demo04-consumer-group-\" + Demo04Message.TOPIC)\n    public void onMessage(Demo04Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n        // 注意，此处抛出一个 RuntimeException 异常，模拟消费失败\n        throw new RuntimeException(\"我就是故意抛出一个异常\");\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo/src/main/java/cn/iocoder/springboot/lab03/kafkademo/message/Demo01Message.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    public static final String TOPIC = \"DEMO_01\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo/src/main/java/cn/iocoder/springboot/lab03/kafkademo/message/Demo04Message.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.message;\n\n/**\n * 示例 04 的 Message 消息\n */\npublic class Demo04Message {\n\n    public static final String TOPIC = \"DEMO_04\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo04Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo04Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo/src/main/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo01Producer.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.producer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.message.Demo01Message;\nimport org.springframework.kafka.core.KafkaTemplate;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.concurrent.ListenableFuture;\n\nimport javax.annotation.Resource;\nimport java.util.concurrent.ExecutionException;\n\n@Component\npublic class Demo01Producer {\n\n    @Resource\n    private KafkaTemplate<Object, Object> kafkaTemplate;\n\n    public SendResult syncSend(Integer id) throws ExecutionException, InterruptedException {\n        // 创建 Demo01Message 消息\n        Demo01Message message = new Demo01Message();\n        message.setId(id);\n        // 同步发送消息\n        return kafkaTemplate.send(Demo01Message.TOPIC, message).get();\n    }\n\n    public ListenableFuture<SendResult<Object, Object>> asyncSend(Integer id) {\n        // 创建 Demo01Message 消息\n        Demo01Message message = new Demo01Message();\n        message.setId(id);\n        // 异步发送消息\n        return kafkaTemplate.send(Demo01Message.TOPIC, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo/src/main/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo04Producer.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.producer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.message.Demo04Message;\nimport org.springframework.kafka.core.KafkaTemplate;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.concurrent.ExecutionException;\n\n@Component\npublic class Demo04Producer {\n\n    @Resource\n    private KafkaTemplate<Object, Object> kafkaTemplate;\n\n    public SendResult syncSend(Integer id) throws ExecutionException, InterruptedException {\n        // 创建 Demo04Message 消息\n        Demo04Message message = new Demo04Message();\n        message.setId(id);\n        // 同步发送消息\n        return kafkaTemplate.send(Demo04Message.TOPIC, message).get();\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo/src/main/resources/application.yaml",
    "content": "spring:\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n    # Kafka Producer 配置项\n    producer:\n      acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。\n      retries: 3 # 发送失败时，重试发送的次数\n      key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化\n      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化\n    # Kafka Consumer 配置项\n    consumer:\n      auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解\n      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer\n      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer\n      properties:\n        spring:\n          json:\n            trusted:\n              packages: cn.iocoder.springboot.lab03.kafkademo.message\n    # Kafka Consumer Listener 监听器配置\n    listener:\n      missing-topics-fatal: false # 消费监听接口监听的主题不存在时，默认会报错。所以通过设置为 false ，解决报错\n\nlogging:\n  level:\n    org:\n      springframework:\n        kafka: ERROR # spring-kafka INFO 日志太多了，所以我们限制只打印 ERROR 级别\n      apache:\n        kafka: ERROR # kafka INFO 日志太多了，所以我们限制只打印 ERROR 级别\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo/src/main/resources/application_bak.yaml",
    "content": "spring:\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n    # Kafka Producer 配置项\n    producer:\n      acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。\n      retries: 3 # 发送失败时，重试发送的次数\n      batch-size: 16384 # 每次批量发送消息的最大数量\n      buffer-memory: 33554432 # 每次批量发送消息的最大内存\n      key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化\n      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化\n    # Kafka Consumer 配置项\n    consumer:\n      enable-auto-commit: false # true-使用 kafka 默认自带的提交模式。false-使用 Spring-Kafka 的自动提交 offset 机制。建议设置为 false 使用 kafka-spring 的机制，分析见 https://juejin.im/entry/5a6e8dea518825732472710c 。\n      auto-commit-interval: 1000 # 在开启 enable-auto-commit 时，自动提交消费进度频率，单位：毫秒。如果 enable-auto-commit 为 false 时候，可以不设置\n      auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解\n      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer\n      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer\n      properties:\n        spring:\n          json:\n            trusted:\n              packages: cn.iocoder.springboot.lab03.kafkademo.message\n    # Kafka Consumer Listener 监听器配置\n    listener:\n      concurrency: 10 # 每个消费者监听器最大并发数，默认为 1 。可以通过设置 n ，这样对于每个监听器就会使用 n 个线程消费消息，提高整体消费速度。详细可参考博客 https://www.jianshu.com/p/ad0e5424edbd 理解。\n\n\nlogging:\n  level:\n    org:\n      springframework:\n        kafka: ERROR # spring-kafka INFO 日志太多了，所以我们限制只打印 ERROR 级别\n      apache:\n        kafka: ERROR # kafka INFO 日志太多了，所以我们限制只打印 ERROR 级别\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo/src/test/java/cn/iocoder/springboot/lab03/kafkademo/package-info.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo;\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo/src/test/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo01ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.producer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.util.concurrent.ListenableFutureCallback;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutionException;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo01ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo01Producer producer;\n\n    @Test\n    public void testSyncSend() throws ExecutionException, InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        SendResult result = producer.syncSend(id);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送结果：[{}]]\", id, result);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testASyncSend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.asyncSend(id).addCallback(new ListenableFutureCallback<SendResult<Object, Object>>() {\n\n            @Override\n            public void onFailure(Throwable e) {\n                logger.info(\"[testASyncSend][发送编号：[{}] 发送异常]]\", id, e);\n            }\n\n            @Override\n            public void onSuccess(SendResult<Object, Object> result) {\n                logger.info(\"[testASyncSend][发送编号：[{}] 发送成功，结果为：[{}]]\", id, result);\n            }\n\n        });\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n//\n//    @Test\n//    public void testOnewaySend() throws InterruptedException {\n//        int id = (int) (System.currentTimeMillis() / 1000);\n//        producer.onewaySend(id);\n//        logger.info(\"[testOnewaySend][发送编号：[{}] 发送完成]\", id);\n//\n//        // 阻塞等待，保证消费\n//        new CountDownLatch(1).await();\n//    }\n\n//    @Test\n//    public void testSyncSendMore() throws ExecutionException, InterruptedException {\n//        for (int i = 0; i < 1000; i++) {\n//            int id = (int) (System.currentTimeMillis() / 1000);\n//            SendResult result = producer.syncSend(id);\n//            logger.info(\"[testSyncSend][发送编号：[{}] 发送结果：[{}]]\", id, result);\n//            Thread.sleep(10);\n//        }\n//\n//        // 阻塞等待，保证消费\n//        new CountDownLatch(1).await();\n//    }\n//\n//    @Test\n//    public void block() throws InterruptedException {\n//        // 阻塞等待，保证消费\n//        new CountDownLatch(1).await();\n//    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo/src/test/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo04ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.producer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutionException;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo04ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo04Producer producer;\n\n    @Test\n    public void testSyncSend() throws ExecutionException, InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        SendResult result = producer.syncSend(id);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送结果：[{}]]\", id, result);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testSyncSendX() throws ExecutionException, InterruptedException {\n        for (int i = 0; i < 100; i++) {\n            int id = (int) (System.currentTimeMillis() / 1000);\n            SendResult result = producer.syncSend(id);\n            logger.info(\"[testSyncSend][发送编号：[{}] 发送结果：[{}]]\", id, result);\n            Thread.sleep(10 * 1000L);\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-ack/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-03-kafka-demo-ack</artifactId>\n\n    <dependencies>\n        <!-- 引入 Spring-Kafka 依赖 -->\n        <!-- 已经内置 kafka-clients 依赖，所以无需重复引入 -->\n        <dependency>\n            <groupId>org.springframework.kafka</groupId>\n            <artifactId>spring-kafka</artifactId>\n            <version>2.3.3.RELEASE</version>\n        </dependency>\n\n        <!-- 实现对 JSON 的自动化配置 -->\n        <!-- 因为，Kafka 对复杂对象的 Message 序列化时，我们会使用到 JSON -->\n        <!--\n            同时，spring-boot-starter-json 引入了 spring-boot-starter ，而 spring-boot-starter 又引入了 spring-boot-autoconfigure 。\n            spring-boot-autoconfigure 实现了 Spring-Kafka 的自动化配置\n         -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-json</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-ack/src/main/java/cn/iocoder/springboot/lab03/kafkademo/Application.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-ack/src/main/java/cn/iocoder/springboot/lab03/kafkademo/consumer/Demo08Consumer.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.consumer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.message.Demo08Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.kafka.annotation.KafkaListener;\nimport org.springframework.kafka.support.Acknowledgment;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo08Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @KafkaListener(topics = Demo08Message.TOPIC,\n            groupId = \"demo08-consumer-group-\" + Demo08Message.TOPIC)\n    public void onMessage(Demo08Message message, Acknowledgment acknowledgment) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n        // 提交消费进度\n        if (message.getId() % 2 == 1) {\n            acknowledgment.acknowledge();\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-ack/src/main/java/cn/iocoder/springboot/lab03/kafkademo/message/Demo08Message.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.message;\n\n/**\n * 示例 08 的 Message 消息\n */\npublic class Demo08Message {\n\n    public static final String TOPIC = \"DEMO_08\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo08Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo08Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-ack/src/main/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo08Producer.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.producer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.message.Demo08Message;\nimport org.springframework.kafka.core.KafkaTemplate;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.concurrent.ListenableFuture;\n\nimport javax.annotation.Resource;\nimport java.util.concurrent.ExecutionException;\n\n@Component\npublic class Demo08Producer {\n\n    @Resource\n    private KafkaTemplate<Object, Object> kafkaTemplate;\n\n    public SendResult syncSend(Integer id) throws ExecutionException, InterruptedException {\n        // 创建 Demo08Message 消息\n        Demo08Message message = new Demo08Message();\n        message.setId(id);\n        // 同步发送消息\n        return kafkaTemplate.send(Demo08Message.TOPIC, message).get();\n    }\n    \n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-ack/src/main/resources/application.yaml",
    "content": "spring:\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n    # Kafka Producer 配置项\n    producer:\n      acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。\n      retries: 3 # 发送失败时，重试发送的次数\n      key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化\n      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化\n    # Kafka Consumer 配置项\n    consumer:\n      auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解\n      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer\n      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer\n      enable-auto-commit: false # 使用 Spring-Kafka 的消费进度的提交机制\n      properties:\n        spring:\n          json:\n            trusted:\n              packages: cn.iocoder.springboot.lab03.kafkademo.message\n    # Kafka Consumer Listener 监听器配置\n    listener:\n      missing-topics-fatal: false # 消费监听接口监听的主题不存在时，默认会报错。所以通过设置为 false ，解决报错\n      ack-mode: MANUAL\n\nlogging:\n  level:\n    org:\n      springframework:\n        kafka: ERROR # spring-kafka INFO 日志太多了，所以我们限制只打印 ERROR 级别\n      apache:\n        kafka: ERROR # kafka INFO 日志太多了，所以我们限制只打印 ERROR 级别\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-ack/src/test/java/cn/iocoder/springboot/lab03/kafkademo/package-info.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo;\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-ack/src/test/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo08ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.producer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.util.concurrent.ListenableFutureCallback;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutionException;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo08ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo08Producer producer;\n\n    @Test\n    public void testSyncSend() throws ExecutionException, InterruptedException {\n        for (int id = 1; id <= 2; id++) {\n            SendResult result = producer.syncSend(id);\n            logger.info(\"[testSyncSend][发送编号：[{}] 发送结果：[{}]]\", id, result);\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-batch/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-03-kafka-demo-batch</artifactId>\n\n    <dependencies>\n        <!-- 引入 Spring-Kafka 依赖 -->\n        <!-- 已经内置 kafka-clients 依赖，所以无需重复引入 -->\n        <dependency>\n            <groupId>org.springframework.kafka</groupId>\n            <artifactId>spring-kafka</artifactId>\n            <version>2.3.3.RELEASE</version>\n        </dependency>\n\n        <!-- 实现对 JSON 的自动化配置 -->\n        <!-- 因为，Kafka 对复杂对象的 Message 序列化时，我们会使用到 JSON -->\n        <!--\n            同时，spring-boot-starter-json 引入了 spring-boot-starter ，而 spring-boot-starter 又引入了 spring-boot-autoconfigure 。\n            spring-boot-autoconfigure 实现了 Spring-Kafka 的自动化配置\n         -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-json</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-batch/src/main/java/cn/iocoder/springboot/lab03/kafkademo/Application.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-batch/src/main/java/cn/iocoder/springboot/lab03/kafkademo/consumer/Demo02Consumer.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.consumer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.message.Demo02Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.kafka.annotation.KafkaListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo02Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @KafkaListener(topics = Demo02Message.TOPIC,\n            groupId = \"demo02-consumer-group-\" + Demo02Message.TOPIC)\n    public void onMessage(Demo02Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-batch/src/main/java/cn/iocoder/springboot/lab03/kafkademo/message/Demo02Message.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo02Message {\n\n    public static final String TOPIC = \"DEMO_02\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo02Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-batch/src/main/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo02Producer.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.producer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.message.Demo02Message;\nimport org.springframework.kafka.core.KafkaTemplate;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.concurrent.ListenableFuture;\n\nimport javax.annotation.Resource;\n\n@Component\npublic class Demo02Producer {\n\n    @Resource\n    private KafkaTemplate<Object, Object> kafkaTemplate;\n\n    public ListenableFuture<SendResult<Object, Object>> asyncSend(Integer id) {\n        // 创建 Demo02Message 消息\n        Demo02Message message = new Demo02Message();\n        message.setId(id);\n        // 异步发送消息\n        return kafkaTemplate.send(Demo02Message.TOPIC, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-batch/src/main/resources/application.yaml",
    "content": "spring:\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n    # Kafka Producer 配置项\n    producer:\n      acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。\n      retries: 3 # 发送失败时，重试发送的次数\n      key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化\n      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化\n      batch-size: 16384 # 每次批量发送消息的最大数量\n      buffer-memory: 33554432 # 每次批量发送消息的最大内存\n      properties:\n        linger:\n          ms: 30000 # 批处理延迟时间上限。这里配置为 30 * 1000 ms 过后，不管是否消息数量是否到达 batch-size 或者消息大小到达 buffer-memory 后，都直接发送一次请求。\n    # Kafka Consumer 配置项\n    consumer:\n      auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解\n      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer\n      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer\n      properties:\n        spring:\n          json:\n            trusted:\n              packages: cn.iocoder.springboot.lab03.kafkademo.message\n    # Kafka Consumer Listener 监听器配置\n    listener:\n      missing-topics-fatal: false # 消费监听接口监听的主题不存在时，默认会报错。所以通过设置为 false ，解决报错\n\nlogging:\n  level:\n    org:\n      springframework:\n        kafka: ERROR # spring-kafka INFO 日志太多了，所以我们限制只打印 ERROR 级别\n      apache:\n        kafka: ERROR # kafka INFO 日志太多了，所以我们限制只打印 ERROR 级别\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-batch/src/test/java/cn/iocoder/springboot/lab03/kafkademo/package-info.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo;\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-batch/src/test/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo02ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.producer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.util.concurrent.ListenableFutureCallback;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo02ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo02Producer producer;\n\n    @Test\n    public void testASyncSend() throws InterruptedException {\n        logger.info(\"[testASyncSend][开始执行]\");\n\n        for (int i = 0; i < 3; i++) {\n            int id = (int) (System.currentTimeMillis() / 1000);\n            producer.asyncSend(id).addCallback(new ListenableFutureCallback<SendResult<Object, Object>>() {\n\n                @Override\n                public void onFailure(Throwable e) {\n                    logger.info(\"[testASyncSend][发送编号：[{}] 发送异常]]\", id, e);\n                }\n\n                @Override\n                public void onSuccess(SendResult<Object, Object> result) {\n                    logger.info(\"[testASyncSend][发送编号：[{}] 发送成功，结果为：[{}]]\", id, result);\n                }\n\n            });\n\n            // 故意每条消息之间，隔离 10 秒\n            Thread.sleep(10 * 1000L);\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-batch-consume/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-03-kafka-demo-batch-consume</artifactId>\n\n    <dependencies>\n        <!-- 引入 Spring-Kafka 依赖 -->\n        <!-- 已经内置 kafka-clients 依赖，所以无需重复引入 -->\n        <dependency>\n            <groupId>org.springframework.kafka</groupId>\n            <artifactId>spring-kafka</artifactId>\n            <version>2.3.3.RELEASE</version>\n        </dependency>\n\n        <!-- 实现对 JSON 的自动化配置 -->\n        <!-- 因为，Kafka 对复杂对象的 Message 序列化时，我们会使用到 JSON -->\n        <!--\n            同时，spring-boot-starter-json 引入了 spring-boot-starter ，而 spring-boot-starter 又引入了 spring-boot-autoconfigure 。\n            spring-boot-autoconfigure 实现了 Spring-Kafka 的自动化配置\n         -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-json</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-batch-consume/src/main/java/cn/iocoder/springboot/lab03/kafkademo/Application.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-batch-consume/src/main/java/cn/iocoder/springboot/lab03/kafkademo/consumer/Demo02Consumer.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.consumer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.message.Demo02Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.kafka.annotation.KafkaListener;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\n\n@Component\npublic class Demo02Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @KafkaListener(topics = Demo02Message.TOPIC,\n            groupId = \"demo02-consumer-group-\" + Demo02Message.TOPIC)\n    public void onMessage(List<Demo02Message> messages) {\n        logger.info(\"[onMessage][线程编号:{} 消息数量：{}]\", Thread.currentThread().getId(), messages.size());\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-batch-consume/src/main/java/cn/iocoder/springboot/lab03/kafkademo/message/Demo02Message.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo02Message {\n\n    public static final String TOPIC = \"DEMO_02\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo02Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-batch-consume/src/main/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo02Producer.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.producer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.message.Demo02Message;\nimport org.springframework.kafka.core.KafkaTemplate;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.concurrent.ListenableFuture;\n\nimport javax.annotation.Resource;\n\n@Component\npublic class Demo02Producer {\n\n    @Resource\n    private KafkaTemplate<Object, Object> kafkaTemplate;\n\n    public ListenableFuture<SendResult<Object, Object>> asyncSend(Integer id) {\n        // 创建 Demo02Message 消息\n        Demo02Message message = new Demo02Message();\n        message.setId(id);\n        // 异步发送消息\n        return kafkaTemplate.send(Demo02Message.TOPIC, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-batch-consume/src/main/resources/application.yaml",
    "content": "spring:\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n    # Kafka Producer 配置项\n    producer:\n      acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。\n      retries: 3 # 发送失败时，重试发送的次数\n      key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化\n      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化\n      batch-size: 16384 # 每次批量发送消息的最大数量\n      buffer-memory: 33554432 # 每次批量发送消息的最大内存\n      properties:\n        linger:\n          ms: 30000 # 批处理延迟时间上限。这里配置为 30 * 1000 ms 过后，不管是否消息数量是否到达 batch-size 或者消息大小到达 buffer-memory 后，都直接发送一次请求。\n    # Kafka Consumer 配置项\n    consumer:\n      auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解\n      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer\n      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer\n      fetch-max-wait: 10000 # poll 一次拉取的阻塞的最大时长，单位：毫秒。这里指的是阻塞拉取需要满足至少 fetch-min-size 条消息\n      fetch-min-size: 10 # poll 一次消息拉取的最小数量\n      max-poll-records: 100 # poll 一次消息拉取的最大数量\n      properties:\n        spring:\n          json:\n            trusted:\n              packages: cn.iocoder.springboot.lab03.kafkademo.message\n    # Kafka Consumer Listener 监听器配置\n    listener:\n      type: BATCH # 监听器类型，默认为 SINGLE ，只监听单条消息。这里我们配置 BATCH ，监听多条消息，批量消费\n      missing-topics-fatal: false # 消费监听接口监听的主题不存在时，默认会报错。所以通过设置为 false ，解决报错\n\nlogging:\n  level:\n    org:\n      springframework:\n        kafka: ERROR # spring-kafka INFO 日志太多了，所以我们限制只打印 ERROR 级别\n      apache:\n        kafka: ERROR # kafka INFO 日志太多了，所以我们限制只打印 ERROR 级别\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-batch-consume/src/test/java/cn/iocoder/springboot/lab03/kafkademo/package-info.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo;\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-batch-consume/src/test/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo02ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.producer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.util.concurrent.ListenableFutureCallback;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo02ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo02Producer producer;\n\n    @Test\n    public void testASyncSend() throws InterruptedException {\n        logger.info(\"[testASyncSend][开始执行]\");\n\n        for (int i = 0; i < 3; i++) {\n            int id = (int) (System.currentTimeMillis() / 1000);\n            producer.asyncSend(id).addCallback(new ListenableFutureCallback<SendResult<Object, Object>>() {\n\n                @Override\n                public void onFailure(Throwable e) {\n                    logger.info(\"[testASyncSend][发送编号：[{}] 发送异常]]\", id, e);\n                }\n\n                @Override\n                public void onSuccess(SendResult<Object, Object> result) {\n                    logger.info(\"[testASyncSend][发送编号：[{}] 发送成功，结果为：[{}]]\", id, result);\n                }\n\n            });\n\n            // 故意每条消息之间，隔离 10 秒\n            Thread.sleep(10 * 1000L);\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-broadcast/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-03-kafka-demo-broadcast</artifactId>\n\n    <dependencies>\n        <!-- 引入 Spring-Kafka 依赖 -->\n        <!-- 已经内置 kafka-clients 依赖，所以无需重复引入 -->\n        <dependency>\n            <groupId>org.springframework.kafka</groupId>\n            <artifactId>spring-kafka</artifactId>\n            <version>2.3.3.RELEASE</version>\n        </dependency>\n\n        <!-- 实现对 JSON 的自动化配置 -->\n        <!-- 因为，Kafka 对复杂对象的 Message 序列化时，我们会使用到 JSON -->\n        <!--\n            同时，spring-boot-starter-json 引入了 spring-boot-starter ，而 spring-boot-starter 又引入了 spring-boot-autoconfigure 。\n            spring-boot-autoconfigure 实现了 Spring-Kafka 的自动化配置\n         -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-json</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-broadcast/src/main/java/cn/iocoder/springboot/lab03/kafkademo/Application.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-broadcast/src/main/java/cn/iocoder/springboot/lab03/kafkademo/consumer/Demo05Consumer.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.consumer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.message.Demo05Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.kafka.annotation.KafkaListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo05Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @KafkaListener(topics = Demo05Message.TOPIC,\n            groupId = \"demo05-consumer-group-\" + Demo05Message.TOPIC + \"-\" + \"#{T(java.util.UUID).randomUUID()}\")\n    public void onMessage(Demo05Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-broadcast/src/main/java/cn/iocoder/springboot/lab03/kafkademo/message/Demo05Message.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.message;\n\n/**\n * 示例 05 的 Message 消息\n */\npublic class Demo05Message {\n\n    public static final String TOPIC = \"DEMO_05\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo05Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo05Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-broadcast/src/main/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo05Producer.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.producer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.message.Demo05Message;\nimport org.springframework.kafka.core.KafkaTemplate;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.concurrent.ExecutionException;\n\n@Component\npublic class Demo05Producer {\n\n    @Resource\n    private KafkaTemplate<Object, Object> kafkaTemplate;\n\n    public SendResult syncSend(Integer id) throws ExecutionException, InterruptedException {\n        // 创建 Demo05Message 消息\n        Demo05Message message = new Demo05Message();\n        message.setId(id);\n        // 同步发送消息\n        return kafkaTemplate.send(Demo05Message.TOPIC, message).get();\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-broadcast/src/main/resources/application.yaml",
    "content": "spring:\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n    # Kafka Producer 配置项\n    producer:\n      acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。\n      retries: 3 # 发送失败时，重试发送的次数\n      key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化\n      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化\n    # Kafka Consumer 配置项\n    consumer:\n      auto-offset-reset: latest # 设置消费者分组最初的消费进度为 latest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解\n      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer\n      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer\n      properties:\n        spring:\n          json:\n            trusted:\n              packages: cn.iocoder.springboot.lab03.kafkademo.message\n    # Kafka Consumer Listener 监听器配置\n    listener:\n      missing-topics-fatal: false # 消费监听接口监听的主题不存在时，默认会报错。所以通过设置为 false ，解决报错\n\nlogging:\n  level:\n    org:\n      springframework:\n        kafka: ERROR # spring-kafka INFO 日志太多了，所以我们限制只打印 ERROR 级别\n      apache:\n        kafka: ERROR # kafka INFO 日志太多了，所以我们限制只打印 ERROR 级别\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-broadcast/src/test/java/cn/iocoder/springboot/lab03/kafkademo/package-info.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo;\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-broadcast/src/test/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo05ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.producer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutionException;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo05ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo05Producer producer;\n\n    @Test\n    public void test() throws InterruptedException {\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testSyncSend() throws ExecutionException, InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        SendResult result = producer.syncSend(id);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送结果：[{}]]\", id, result);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-concurrency/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-03-kafka-demo-concurrency</artifactId>\n\n    <dependencies>\n        <!-- 引入 Spring-Kafka 依赖 -->\n        <!-- 已经内置 kafka-clients 依赖，所以无需重复引入 -->\n        <dependency>\n            <groupId>org.springframework.kafka</groupId>\n            <artifactId>spring-kafka</artifactId>\n            <version>2.3.3.RELEASE</version>\n        </dependency>\n\n        <!-- 实现对 JSON 的自动化配置 -->\n        <!-- 因为，Kafka 对复杂对象的 Message 序列化时，我们会使用到 JSON -->\n        <!--\n            同时，spring-boot-starter-json 引入了 spring-boot-starter ，而 spring-boot-starter 又引入了 spring-boot-autoconfigure 。\n            spring-boot-autoconfigure 实现了 Spring-Kafka 的自动化配置\n         -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-json</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-concurrency/src/main/java/cn/iocoder/springboot/lab03/kafkademo/Application.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-concurrency/src/main/java/cn/iocoder/springboot/lab03/kafkademo/consumer/Demo06Consumer.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.consumer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.message.Demo06Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.kafka.annotation.KafkaListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo06Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @KafkaListener(topics = Demo06Message.TOPIC,\n            groupId = \"demo06-consumer-group-\" + Demo06Message.TOPIC,\n            concurrency = \"2\")\n    public void onMessage(Demo06Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-concurrency/src/main/java/cn/iocoder/springboot/lab03/kafkademo/message/Demo06Message.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.message;\n\n/**\n * 示例 06 的 Message 消息\n */\npublic class Demo06Message {\n\n    public static final String TOPIC = \"DEMO_06\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo06Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo06Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-concurrency/src/main/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo06Producer.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.producer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.message.Demo06Message;\nimport org.springframework.kafka.core.KafkaTemplate;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.concurrent.ExecutionException;\n\n@Component\npublic class Demo06Producer {\n\n    @Resource\n    private KafkaTemplate<Object, Object> kafkaTemplate;\n\n    public SendResult syncSend(Integer id) throws ExecutionException, InterruptedException {\n        // 创建 Demo01Message 消息\n        Demo06Message message = new Demo06Message();\n        message.setId(id);\n        // 同步发送消息\n        return kafkaTemplate.send(Demo06Message.TOPIC, message).get();\n    }\n\n    public SendResult syncSendOrderly(Integer id) throws ExecutionException, InterruptedException {\n        // 创建 Demo01Message 消息\n        Demo06Message message = new Demo06Message();\n        message.setId(id);\n        // 同步发送消息\n        // 因为我们使用 String 的方式序列化 key ，所以需要将 id 转换成 String\n        return kafkaTemplate.send(Demo06Message.TOPIC, String.valueOf(id), message).get();\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-concurrency/src/main/resources/application.yaml",
    "content": "spring:\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n    # Kafka Producer 配置项\n    producer:\n      acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。\n      retries: 3 # 发送失败时，重试发送的次数\n      key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化\n      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化\n    # Kafka Consumer 配置项\n    consumer:\n      auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解\n      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer\n      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer\n      properties:\n        spring:\n          json:\n            trusted:\n              packages: cn.iocoder.springboot.lab03.kafkademo.message\n    # Kafka Consumer Listener 监听器配置\n    listener:\n      missing-topics-fatal: false # 消费监听接口监听的主题不存在时，默认会报错。所以通过设置为 false ，解决报错\n\nlogging:\n  level:\n    org:\n      springframework:\n        kafka: ERROR # spring-kafka INFO 日志太多了，所以我们限制只打印 ERROR 级别\n      apache:\n        kafka: ERROR # kafka INFO 日志太多了，所以我们限制只打印 ERROR 级别\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-concurrency/src/test/java/cn/iocoder/springboot/lab03/kafkademo/package-info.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo;\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-concurrency/src/test/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo06ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.producer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutionException;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo06ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo06Producer producer;\n\n    @Test\n    public void testSyncSend() throws ExecutionException, InterruptedException {\n        for (int i = 0; i < 10; i++) {\n            int id = (int) (System.currentTimeMillis() / 1000);\n            SendResult result = producer.syncSend(id);\n//        logger.info(\"[testSyncSend][发送编号：[{}] 发送结果：[{}]]\", id, result);\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testSyncSendOrderly() throws ExecutionException, InterruptedException {\n        for (int i = 0; i < 10; i++) {\n            int id = 1;\n            SendResult result = producer.syncSendOrderly(id);\n            logger.info(\"[testSyncSend][发送编号：[{}] 发送队列：[{}]]\", id, result.getRecordMetadata().partition());\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-transaction/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-03-kafka-demo-transaction</artifactId>\n\n    <dependencies>\n        <!-- 引入 Spring-Kafka 依赖 -->\n        <!-- 已经内置 kafka-clients 依赖，所以无需重复引入 -->\n        <dependency>\n            <groupId>org.springframework.kafka</groupId>\n            <artifactId>spring-kafka</artifactId>\n            <version>2.3.3.RELEASE</version>\n        </dependency>\n\n        <!-- 实现对 JSON 的自动化配置 -->\n        <!-- 因为，Kafka 对复杂对象的 Message 序列化时，我们会使用到 JSON -->\n        <!--\n            同时，spring-boot-starter-json 引入了 spring-boot-starter ，而 spring-boot-starter 又引入了 spring-boot-autoconfigure 。\n            spring-boot-autoconfigure 实现了 Spring-Kafka 的自动化配置\n         -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-json</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-transaction/src/main/java/cn/iocoder/springboot/lab03/kafkademo/Application.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-transaction/src/main/java/cn/iocoder/springboot/lab03/kafkademo/consumer/Demo07Consumer.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.consumer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.message.Demo07Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.kafka.annotation.KafkaListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo07Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @KafkaListener(topics = Demo07Message.TOPIC,\n            groupId = \"demo07-consumer-group-\" + Demo07Message.TOPIC)\n    public void onMessage(Demo07Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-transaction/src/main/java/cn/iocoder/springboot/lab03/kafkademo/message/Demo07Message.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.message;\n\n/**\n * 示例 07 的 Message 消息\n */\npublic class Demo07Message {\n\n    public static final String TOPIC = \"DEMO_07\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo07Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo07Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-transaction/src/main/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo07Producer.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.producer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.message.Demo07Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.kafka.core.KafkaOperations;\nimport org.springframework.kafka.core.KafkaTemplate;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.concurrent.ListenableFuture;\n\nimport javax.annotation.Resource;\nimport java.util.concurrent.ExecutionException;\n\n@Component\npublic class Demo07Producer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Resource\n    private KafkaTemplate<Object, Object> kafkaTemplate;\n\n    public SendResult syncSend(Integer id) throws ExecutionException, InterruptedException {\n        // 创建 Demo07Message 消息\n        Demo07Message message = new Demo07Message();\n        message.setId(id);\n        // 同步发送消息\n        return kafkaTemplate.send(Demo07Message.TOPIC, message).get();\n    }\n\n    public String syncSendInTransaction(Integer id, Runnable runner) throws ExecutionException, InterruptedException {\n        return kafkaTemplate.executeInTransaction(new KafkaOperations.OperationsCallback<Object, Object, String>() {\n\n            @Override\n            public String doInOperations(KafkaOperations<Object, Object> kafkaOperations) {\n                // 创建 Demo07Message 消息\n                Demo07Message message = new Demo07Message();\n                message.setId(id);\n                try {\n                    SendResult<Object, Object> sendResult = kafkaOperations.send(Demo07Message.TOPIC, message).get();\n                    logger.info(\"[doInOperations][发送编号：[{}] 发送结果：[{}]]\", id, sendResult);\n                } catch (Exception e) {\n                    throw new RuntimeException(e);\n                }\n\n                // 本地业务逻辑... biubiubiu\n                runner.run();\n\n                // 返回结果\n                return \"success\";\n            }\n\n        });\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-transaction/src/main/resources/application.yaml",
    "content": "spring:\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n    # Kafka Producer 配置项\n    producer:\n      acks: all # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。\n      retries: 3 # 发送失败时，重试发送的次数\n      key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化\n      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化\n      transaction-id-prefix: demo. # 事务编号前缀\n    # Kafka Consumer 配置项\n    consumer:\n      auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解\n      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer\n      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer\n      properties:\n        spring:\n          json:\n            trusted:\n              packages: cn.iocoder.springboot.lab03.kafkademo.message\n        isolation:\n          level: read_committed # 读取已提交的消息\n    # Kafka Consumer Listener 监听器配置\n    listener:\n      missing-topics-fatal: false # 消费监听接口监听的主题不存在时，默认会报错。所以通过设置为 false ，解决报错\n\nlogging:\n  level:\n    org:\n      springframework:\n        kafka: ERROR # spring-kafka INFO 日志太多了，所以我们限制只打印 ERROR 级别\n      apache:\n        kafka: ERROR # kafka INFO 日志太多了，所以我们限制只打印 ERROR 级别\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-transaction/src/main/resources/application_bak.yaml",
    "content": "spring:\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n    # Kafka Producer 配置项\n    producer:\n      acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。\n      retries: 3 # 发送失败时，重试发送的次数\n      batch-size: 16384 # 每次批量发送消息的最大数量\n      buffer-memory: 33554432 # 每次批量发送消息的最大内存\n      key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化\n      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化\n    # Kafka Consumer 配置项\n    consumer:\n      enable-auto-commit: false # true-使用 kafka 默认自带的提交模式。false-使用 Spring-Kafka 的自动提交 offset 机制。建议设置为 false 使用 kafka-spring 的机制，分析见 https://juejin.im/entry/5a6e8dea518825732472710c 。\n      auto-commit-interval: 1000 # 在开启 enable-auto-commit 时，自动提交消费进度频率，单位：毫秒。如果 enable-auto-commit 为 false 时候，可以不设置\n      auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解\n      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer\n      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer\n      properties:\n        spring:\n          json:\n            trusted:\n              packages: cn.iocoder.springboot.lab03.kafkademo.message\n    # Kafka Consumer Listener 监听器配置\n    listener:\n      concurrency: 10 # 每个消费者监听器最大并发数，默认为 1 。可以通过设置 n ，这样对于每个监听器就会使用 n 个线程消费消息，提高整体消费速度。详细可参考博客 https://www.jianshu.com/p/ad0e5424edbd 理解。\n    # Producer 配置项\n\nlogging:\n  level:\n    org:\n      springframework:\n        kafka: ERROR # spring-kafka INFO 日志太多了，所以我们限制只打印 ERROR 级别\n      apache:\n        kafka: ERROR # kafka INFO 日志太多了，所以我们限制只打印 ERROR 级别\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-transaction/src/test/java/cn/iocoder/springboot/lab03/kafkademo/package-info.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo;\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-demo-transaction/src/test/java/cn/iocoder/springboot/lab03/kafkademo/producer/Demo07ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo.producer;\n\nimport cn.iocoder.springboot.lab03.kafkademo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.util.concurrent.ListenableFutureCallback;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.ExecutionException;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo07ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo07Producer producer;\n\n    @Test\n    public void testSyncSend() throws ExecutionException, InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        SendResult result = producer.syncSend(id);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送结果：[{}]]\", id, result);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testSyncSendInTransaction() throws ExecutionException, InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSendInTransaction(id, new Runnable() {\n\n            @Override\n            public void run() {\n                logger.info(\"[run][我要开始睡觉了]\");\n                try {\n                    Thread.sleep(10 * 1000L);\n                } catch (InterruptedException e) {\n                    throw new RuntimeException(e);\n                }\n                logger.info(\"[run][我睡醒了]\");\n            }\n\n        });\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-native/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-03-kafka-native</artifactId>\n\n    <dependencies>\n        <!-- 引入 Kafka 客户端依赖 -->\n        <dependency>\n            <groupId>org.apache.kafka</groupId>\n            <artifactId>kafka-clients</artifactId>\n            <version>2.3.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-native/src/main/java/cn/iocoder/springboot/lab03/kafkademo/ConsumerMain.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo;\n\nimport org.apache.kafka.clients.consumer.Consumer;\nimport org.apache.kafka.clients.consumer.ConsumerRecord;\nimport org.apache.kafka.clients.consumer.ConsumerRecords;\nimport org.apache.kafka.clients.consumer.KafkaConsumer;\nimport org.apache.kafka.common.serialization.StringDeserializer;\n\nimport java.time.Duration;\nimport java.util.Collections;\nimport java.util.Properties;\n\npublic class ConsumerMain {\n\n    private static Consumer<String, String> createConsumer() {\n        // 设置 Producer 的属性\n        Properties properties = new Properties();\n        properties.put(\"bootstrap.servers\", \"127.0.0.1:9092\"); // 设置 Broker 的地址\n        properties.put(\"group.id\", \"demo-consumer-group\"); // 消费者分组\n        properties.put(\"auto.offset.reset\", \"earliest\"); // 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解\n        properties.put(\"enable.auto.commit\", true); // 是否自动提交消费进度\n        properties.put(\"auto.commit.interval.ms\", \"1000\"); // 自动提交消费进度频率\n        properties.put(\"key.deserializer\", StringDeserializer.class.getName()); // 消息的 key 的反序列化方式\n        properties.put(\"value.deserializer\", StringDeserializer.class.getName()); // 消息的 value 的反序列化方式\n\n        // 创建 KafkaProducer 对象\n        // 因为我们消息的 key 和 value 都使用 String 类型，所以创建的 Producer 是 <String, String> 的泛型。\n        return new KafkaConsumer<>(properties);\n    }\n\n    public static void main(String[] args) {\n        // 创建 KafkaConsumer 对象\n        Consumer<String, String> consumer = createConsumer();\n\n        // 订阅消息\n        consumer.subscribe(Collections.singleton(\"TestTopic\"));\n\n        // 拉取消息\n        while (true) {\n            // 拉取消息。如果拉取不到消息，阻塞等待最多 10 秒，或者等待拉取到消息。\n            ConsumerRecords records = consumer.poll(Duration.ofSeconds(10));\n            // 遍历处理消息\n            records.forEach(new java.util.function.Consumer<ConsumerRecord>() {\n\n                @Override\n                public void accept(ConsumerRecord record) {\n                    System.out.println(record.key() + \"\\t\" + record.value());\n                }\n\n            });\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/lab-03-kafka-native/src/main/java/cn/iocoder/springboot/lab03/kafkademo/ProducerMain.java",
    "content": "package cn.iocoder.springboot.lab03.kafkademo;\n\nimport org.apache.kafka.clients.producer.KafkaProducer;\nimport org.apache.kafka.clients.producer.Producer;\nimport org.apache.kafka.clients.producer.ProducerRecord;\nimport org.apache.kafka.clients.producer.RecordMetadata;\nimport org.apache.kafka.common.serialization.StringSerializer;\n\nimport java.util.Properties;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\n\npublic class ProducerMain {\n\n    private static Producer<String, String> createProducer() {\n        // 设置 Producer 的属性\n        Properties properties = new Properties();\n        properties.put(\"bootstrap.servers\", \"127.0.0.1:9092\"); // 设置 Broker 的地址\n        properties.put(\"acks\", \"1\"); // 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。\n        properties.put(\"retries\", 3); // 发送失败时，重试发送的次数\n//        properties.put(\"batch.size\", 16384);\n//        properties.put(\"linger.ms\", 1);\n//        properties.put(\"client.id\", \"DemoProducer\");\n//        properties.put(\"buffer.memory\", 33554432);\n        properties.put(\"key.serializer\", StringSerializer.class.getName()); // 消息的 key 的序列化方式\n        properties.put(\"value.serializer\", StringSerializer.class.getName()); // 消息的 value 的序列化方式\n\n        // 创建 KafkaProducer 对象\n        // 因为我们消息的 key 和 value 都使用 String 类型，所以创建的 Producer 是 <String, String> 的泛型。\n        return new KafkaProducer<>(properties);\n    }\n\n    public static void main(String[] args) throws ExecutionException, InterruptedException {\n        // 创建 KafkaProducer 对象\n        Producer<String, String> producer = createProducer();\n\n        // 创建消息。传入的三个参数，分别是 Topic ，消息的 key ，消息的 message 。\n        ProducerRecord<String, String> message = new ProducerRecord<>(\"TestTopic\", \"key\", \"yudaoyuanma\");\n\n        // 同步发送消息\n        Future<RecordMetadata> sendResultFuture = producer.send(message);\n        RecordMetadata result = sendResultFuture.get();\n        System.out.println(\"message sent to \" + result.topic() + \", partition \" + result.partition() + \", offset \" + result.offset());\n    }\n\n}\n"
  },
  {
    "path": "lab-03-kafka/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>1.5.9.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-03-kafka</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-03-kafka-native</module>\n        <module>lab-03-kafka-demo</module>\n        <module>lab-03-kafka-demo-batch</module>\n        <module>lab-03-kafka-demo-batch-consume</module>\n        <module>lab-03-kafka-demo-broadcast</module>\n        <module>lab-03-kafka-demo-concurrency</module>\n        <module>lab-03-kafka-demo-transaction</module>\n        <module>lab-03-kafka-demo-ack</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-03-kafka/《芋道 Spring Boot 消息队列 Kafka 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Kafka/?github>\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-04-rabbitmq-consume-retry</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RabbitMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.config;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo07Message;\nimport org.springframework.amqp.core.*;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class RabbitConfig {\n\n    /**\n     * Direct Exchange 示例的配置类\n     */\n    public static class DirectExchangeDemoConfiguration {\n\n        // 创建 Queue\n        @Bean\n        public Queue demo07Queue() {\n            return QueueBuilder.durable(Demo07Message.QUEUE) // durable: 是否持久化\n                    .exclusive() // exclusive: 是否排它\n                    .autoDelete() // autoDelete: 是否自动删除\n                    .deadLetterExchange(Demo07Message.EXCHANGE)\n                    .deadLetterRoutingKey(Demo07Message.DEAD_ROUTING_KEY)\n                    .build();\n        }\n\n        // 创建 Dead Queue\n        @Bean\n        public Queue demo07DeadQueue() {\n            return new Queue(Demo07Message.DEAD_QUEUE, // Queue 名字\n                    true, // durable: 是否持久化\n                    false, // exclusive: 是否排它\n                    false); // autoDelete: 是否自动删除\n        }\n\n        // 创建 Direct Exchange\n        @Bean\n        public DirectExchange demo07Exchange() {\n            return new DirectExchange(Demo07Message.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n        // 创建 Binding\n        // Exchange：Demo07Message.EXCHANGE\n        // Routing key：Demo07Message.ROUTING_KEY\n        // Queue：Demo07Message.QUEUE\n        @Bean\n        public Binding demo07Binding() {\n            return BindingBuilder.bind(demo07Queue()).to(demo07Exchange()).with(Demo07Message.ROUTING_KEY);\n        }\n\n        // 创建 Dead Binding\n        // Exchange：Demo07Message.EXCHANGE\n        // Routing key：Demo07Message.DEAD_ROUTING_KEY\n        // Queue：Demo07Message.DEAD_QUEUE\n        @Bean\n        public Binding demo07DeadBinding() {\n            return BindingBuilder.bind(demo07DeadQueue()).to(demo07Exchange()).with(Demo07Message.DEAD_ROUTING_KEY);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo07Consumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo07Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(queues = Demo07Message.QUEUE)\npublic class Demo07Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(Demo07Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n        // <X> 注意，此处抛出一个 RuntimeException 异常，模拟消费失败\n        throw new RuntimeException(\"我就是故意抛出一个异常\");\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo07DeadConsumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo07Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(queues = Demo07Message.DEAD_QUEUE)\npublic class Demo07DeadConsumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(Demo07Message message) {\n        logger.info(\"[onMessage][【死信队列】线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo07Message.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo07Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_07\"; // 正常队列\n    public static final String DEAD_QUEUE = \"DEAD_QUEUE_DEMO_07\"; // 死信队列\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_07\";\n\n    public static final String ROUTING_KEY = \"ROUTING_KEY_07\"; // 正常路由键\n    public static final String DEAD_ROUTING_KEY = \"DEAD_ROUTING_KEY_07\"; // 死信路由键\n\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo07Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo07Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo07Producer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo07Message;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo07Producer {\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 Demo07Message 消息\n        Demo07Message message = new Demo07Message();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(Demo07Message.EXCHANGE, Demo07Message.ROUTING_KEY, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/src/main/resources/application.yaml",
    "content": "spring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n    template:\n      # 对应 RabbitProperties.Retry 类\n      retry:\n        enabled: true # 开启发送机制\n        max-attempts: 3 # 最大重试次数。默认为 3 。\n        initial-interval: 1000 # 重试间隔，单位为毫秒。默认为 1000 。\n    listener:\n      simple:\n        # 对应 RabbitProperties.ListenerRetry 类\n        retry:\n          enabled: true # 开启消费重试机制\n          max-attempts: 3 # 最大重试次数。默认为 3 。\n          initial-interval: 1000 # 重试间隔，单位为毫秒。默认为 1000 。\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-consume-retry/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo07ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo07ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo07Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSend(id);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-04-rabbitmq-demo</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RabbitMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.scheduling.annotation.EnableAsync;\n\n@SpringBootApplication\n@EnableAsync // 开启异步\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.config;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo01Message;\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo02Message;\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo03Message;\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo04Message;\nimport org.springframework.amqp.core.*;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class RabbitConfig {\n\n    /**\n     * Direct Exchange 示例的配置类\n     */\n    public static class DirectExchangeDemoConfiguration {\n\n        // 创建 Queue\n        @Bean\n        public Queue demo01Queue() {\n            return new Queue(Demo01Message.QUEUE, // Queue 名字\n                    true, // durable: 是否持久化\n                    false, // exclusive: 是否排它\n                    false); // autoDelete: 是否自动删除\n        }\n\n        // 创建 Direct Exchange\n        @Bean\n        public DirectExchange demo01Exchange() {\n            return new DirectExchange(Demo01Message.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n        // 创建 Binding\n        // Exchange：Demo01Message.EXCHANGE\n        // Routing key：Demo01Message.ROUTING_KEY\n        // Queue：Demo01Message.QUEUE\n        @Bean\n        public Binding demo01Binding() {\n            return BindingBuilder.bind(demo01Queue()).to(demo01Exchange()).with(Demo01Message.ROUTING_KEY);\n        }\n\n    }\n\n    /**\n     * Topic Exchange 示例的配置类\n     */\n    public static class TopicExchangeDemoConfiguration {\n\n        // 创建 Queue\n        @Bean\n        public Queue demo02Queue() {\n            return new Queue(Demo02Message.QUEUE, // Queue 名字\n                    true, // durable: 是否持久化\n                    false, // exclusive: 是否排它\n                    false); // autoDelete: 是否自动删除\n        }\n\n        // 创建 Topic Exchange\n        @Bean\n        public TopicExchange demo02Exchange() {\n            return new TopicExchange(Demo02Message.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n        // 创建 Binding\n        // Exchange：Demo02Message.EXCHANGE\n        // Routing key：Demo02Message.ROUTING_KEY\n        // Queue：Demo02Message.QUEUE\n        @Bean\n        public Binding demo02Binding() {\n            return BindingBuilder.bind(demo02Queue()).to(demo02Exchange()).with(Demo02Message.ROUTING_KEY);\n        }\n\n    }\n\n    /**\n     * Fanout Exchange 示例的配置类\n     */\n    public static class FanoutExchangeDemoConfiguration {\n\n        // 创建 Queue A\n        @Bean\n        public Queue demo03QueueA() {\n            return new Queue(Demo03Message.QUEUE_A, // Queue 名字\n                    true, // durable: 是否持久化\n                    false, // exclusive: 是否排它\n                    false); // autoDelete: 是否自动删除\n        }\n\n        // 创建 Queue B\n        @Bean\n        public Queue demo03QueueB() {\n            return new Queue(Demo03Message.QUEUE_B, // Queue 名字\n                    true, // durable: 是否持久化\n                    false, // exclusive: 是否排它\n                    false); // autoDelete: 是否自动删除\n        }\n\n        // 创建 Fanout Exchange\n        @Bean\n        public FanoutExchange demo03Exchange() {\n            return new FanoutExchange(Demo03Message.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n        // 创建 Binding A\n        // Exchange：Demo03Message.EXCHANGE\n        // Queue：Demo03Message.QUEUE_A\n        @Bean\n        public Binding demo03BindingA() {\n            return BindingBuilder.bind(demo03QueueA()).to(demo03Exchange());\n        }\n\n        // 创建 Binding B\n        // Exchange：Demo03Message.EXCHANGE\n        // Queue：Demo03Message.QUEUE_B\n        @Bean\n        public Binding demo03BindingB() {\n            return BindingBuilder.bind(demo03QueueB()).to(demo03Exchange());\n        }\n\n    }\n\n    /**\n     * Headers Exchange 示例的配置类\n     */\n    public static class HeadersExchangeDemoConfiguration {\n\n        // 创建 Queue\n        @Bean\n        public Queue demo04Queue() {\n            return new Queue(Demo04Message.QUEUE, // Queue 名字\n                    true, // durable: 是否持久化\n                    false, // exclusive: 是否排它\n                    false); // autoDelete: 是否自动删除\n        }\n\n        // 创建 Headers Exchange\n        @Bean\n        public HeadersExchange demo04Exchange() {\n            return new HeadersExchange(Demo04Message.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n        // 创建 Binding\n        // Exchange：Demo04Message.EXCHANGE\n        // Queue：Demo04Message.QUEUE\n        // Headers: Demo04Message.HEADER_KEY + Demo04Message.HEADER_VALUE\n        @Bean\n        public Binding demo4Binding() {\n            return BindingBuilder.bind(demo04Queue()).to(demo04Exchange())\n                    .where(Demo04Message.HEADER_KEY).matches(Demo04Message.HEADER_VALUE); // 配置 Headers 匹配\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo01Consumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(queues = Demo01Message.QUEUE)\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n//    @RabbitHandler(isDefault = true)\n//    public void onMessage(org.springframework.amqp.core.Message message) {\n//        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n//    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo02Consumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo02Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(queues = Demo02Message.QUEUE)\npublic class Demo02Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(Demo02Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo03ConsumerA.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo03Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(queues = Demo03Message.QUEUE_A)\npublic class Demo03ConsumerA {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(Demo03Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo03ConsumerB.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo03Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(queues = Demo03Message.QUEUE_B)\npublic class Demo03ConsumerB {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(Demo03Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo04Consumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo04Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(queues = Demo04Message.QUEUE)\npublic class Demo04Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(Demo04Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n//    @RabbitHandler(isDefault = true)\n//    public void onMessage(org.springframework.amqp.core.Message message) {\n//        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n//    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo01Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_01\";\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_01\";\n\n    public static final String ROUTING_KEY = \"ROUTING_KEY_01\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo02Message.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo02Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_02\";\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_02\";\n\n    public static final String ROUTING_KEY = \"#.yu.nai\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo02Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo02Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo03Message.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo03Message implements Serializable {\n\n    public static final String QUEUE_A = \"QUEUE_DEMO_03_A\";\n    public static final String QUEUE_B = \"QUEUE_DEMO_03_B\";\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_03\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo03Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo03Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo04Message.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo04Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_04_A\";\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_04\";\n\n    public static final String HEADER_KEY = \"color\";\n    public static final String HEADER_VALUE = \"red\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo04Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo04Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo01Producer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo01Message;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.scheduling.annotation.AsyncResult;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.concurrent.ListenableFuture;\n\n@Component\npublic class Demo01Producer {\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 Demo01Message 消息\n        Demo01Message message = new Demo01Message();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(Demo01Message.EXCHANGE, Demo01Message.ROUTING_KEY, message);\n    }\n\n    public void syncSendDefault(Integer id) {\n        // 创建 Demo01Message 消息\n        Demo01Message message = new Demo01Message();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(Demo01Message.QUEUE, message);\n    }\n\n    @Async\n    public ListenableFuture<Void> asyncSend(Integer id) {\n        try {\n            // 发送消息\n            this.syncSend(id);\n            // 返回成功的 Future\n            return AsyncResult.forValue(null);\n        } catch (Throwable ex) {\n            // 返回异常的 Future\n            return AsyncResult.forExecutionException(ex);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo02Producer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo02Message;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo02Producer {\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public void syncSend(Integer id, String routingKey) {\n        // 创建 Demo02Message 消息\n        Demo02Message message = new Demo02Message();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(Demo02Message.EXCHANGE, routingKey, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo03Producer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo03Message;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo03Producer {\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 Demo03Message 消息\n        Demo03Message message = new Demo03Message();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(Demo03Message.EXCHANGE, null, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo04Producer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo04Message;\nimport org.springframework.amqp.core.Message;\nimport org.springframework.amqp.core.MessageProperties;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo04Producer {\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public void syncSend(Integer id, String headerValue) {\n        // 创建 MessageProperties 属性\n        MessageProperties messageProperties = new MessageProperties();\n        messageProperties.setHeader(Demo04Message.HEADER_KEY, headerValue); // 设置 header\n        // 创建 Message 消息\n        Message message = rabbitTemplate.getMessageConverter().toMessage(\n                new Demo04Message().setId(id), messageProperties);\n        // 同步发送消息\n        rabbitTemplate.send(Demo04Message.EXCHANGE, null, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/main/resources/application.yaml",
    "content": "spring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo01ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.util.concurrent.ListenableFutureCallback;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo01ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo01Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSend(id);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void tesSyncSendDefault() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSendDefault(id);\n        logger.info(\"[tesSyncSendDefault][发送编号：[{}] 发送成功]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testAsyncSend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.asyncSend(id).addCallback(new ListenableFutureCallback<Void>() {\n\n            @Override\n            public void onFailure(Throwable e) {\n                logger.info(\"[testASyncSend][发送编号：[{}] 发送异常]]\", id, e);\n            }\n\n            @Override\n            public void onSuccess(Void aVoid) {\n                logger.info(\"[testASyncSend][发送编号：[{}] 发送成功，发送成功]\", id);\n            }\n\n        });\n        logger.info(\"[testASyncSend][发送编号：[{}] 调用完成]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void nothing() throws InterruptedException {\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void nothing02() throws InterruptedException {\n        for (int i = 0; i < 1000; i++) {\n            int id = (int) (System.currentTimeMillis() / 1000);\n            producer.syncSend(id);\n            logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n            Thread.sleep(5000L);\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo02ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo02ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo02Producer producer;\n\n    @Test\n    public void testSyncSendSuccess() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSend(id, \"da.yu.nai\");\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testSyncSendFailure() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSend(id, \"yu.nai.shuai\");\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo03ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo03ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo03Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSend(id);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo04ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo04Message;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo04ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo04Producer producer;\n\n    @Test\n    public void testSyncSendSuccess() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSend(id, Demo04Message.HEADER_VALUE);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testSyncSendFailure() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSend(id, \"error\");\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-ack/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-04-rabbitmq-demo-ack</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RabbitMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-ack/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.scheduling.annotation.EnableAsync;\n\n@SpringBootApplication\n@EnableAsync // 开启异步\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-ack/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.config;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo12Message;\nimport org.springframework.amqp.core.Binding;\nimport org.springframework.amqp.core.BindingBuilder;\nimport org.springframework.amqp.core.DirectExchange;\nimport org.springframework.amqp.core.Queue;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class RabbitConfig {\n\n    /**\n     * Direct Exchange 示例的配置类\n     */\n    public static class DirectExchangeDemoConfiguration {\n\n        // 创建 Queue\n        @Bean\n        public Queue demo12Queue() {\n            return new Queue(Demo12Message.QUEUE, // Queue 名字\n                    true, // durable: 是否持久化\n                    false, // exclusive: 是否排它\n                    false); // autoDelete: 是否自动删除\n        }\n\n        // 创建 Direct Exchange\n        @Bean\n        public DirectExchange demo12Exchange() {\n            return new DirectExchange(Demo12Message.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n        // 创建 Binding\n        // Exchange：Demo12Message.EXCHANGE\n        // Routing key：Demo12Message.ROUTING_KEY\n        // Queue：Demo12Message.QUEUE\n        @Bean\n        public Binding demo12Binding() {\n            return BindingBuilder.bind(demo12Queue()).to(demo12Exchange()).with(Demo12Message.ROUTING_KEY);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-ack/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo12Consumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo12Message;\nimport com.rabbitmq.client.Channel;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.amqp.support.AmqpHeaders;\nimport org.springframework.messaging.handler.annotation.Header;\nimport org.springframework.stereotype.Component;\n\nimport java.io.IOException;\n\n@Component\n@RabbitListener(queues = Demo12Message.QUEUE)\npublic class Demo12Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(Demo12Message message, Channel channel,\n                          @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws IOException {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n        // 提交消费进度\n        if (message.getId() % 2 == 1) {\n            // ack 确认消息\n            // 第二个参数 multiple ，用于批量确认消息，为了减少网络流量，手动确认可以被批处。\n            // 1. 当 multiple 为 true 时，则可以一次性确认 deliveryTag 小于等于传入值的所有消息\n            // 2. 当 multiple 为 false 时，则只确认当前 deliveryTag 对应的消息\n            channel.basicAck(deliveryTag, false);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-ack/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo12Message.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo12Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_12\";\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_12\";\n\n    public static final String ROUTING_KEY = \"ROUTING_KEY_12\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo12Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo12Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-ack/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo12Producer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo12Message;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo12Producer {\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 Demo12Message 消息\n        Demo12Message message = new Demo12Message();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(Demo12Message.EXCHANGE, Demo12Message.ROUTING_KEY, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-ack/src/main/resources/application.yaml",
    "content": "spring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n    listener:\n      simple:\n        acknowledge-mode: manual # 配置 Consumer 手动提交\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-ack/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-ack/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo12ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo12ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo12Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        for (int id = 1; id <= 2; id++) {\n            producer.syncSend(id);\n            logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-04-rabbitmq-demo-batch</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RabbitMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.scheduling.annotation.EnableAsync;\n\n@SpringBootApplication\n@EnableAsync // 开启异步\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.config;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo05Message;\nimport org.springframework.amqp.core.Binding;\nimport org.springframework.amqp.core.BindingBuilder;\nimport org.springframework.amqp.core.DirectExchange;\nimport org.springframework.amqp.core.Queue;\nimport org.springframework.amqp.rabbit.batch.BatchingStrategy;\nimport org.springframework.amqp.rabbit.batch.SimpleBatchingStrategy;\nimport org.springframework.amqp.rabbit.connection.ConnectionFactory;\nimport org.springframework.amqp.rabbit.core.BatchingRabbitTemplate;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.scheduling.TaskScheduler;\nimport org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;\n\n@Configuration\npublic class RabbitConfig {\n\n    /**\n     * Direct Exchange 示例的配置类\n     */\n    public static class DirectExchangeDemoConfiguration {\n\n        // 创建 Queue\n        @Bean\n        public Queue demo05Queue() {\n            return new Queue(Demo05Message.QUEUE, // Queue 名字\n                    true, // durable: 是否持久化\n                    false, // exclusive: 是否排它\n                    false); // autoDelete: 是否自动删除\n        }\n\n        // 创建 Direct Exchange\n        @Bean\n        public DirectExchange demo05Exchange() {\n            return new DirectExchange(Demo05Message.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n        // 创建 Binding\n        // Exchange：Demo05Message.EXCHANGE\n        // Routing key：Demo05Message.ROUTING_KEY\n        // Queue：Demo05Message.QUEUE\n        @Bean\n        public Binding demo05Binding() {\n            return BindingBuilder.bind(demo05Queue()).to(demo05Exchange()).with(Demo05Message.ROUTING_KEY);\n        }\n\n    }\n\n    @Bean\n    public BatchingRabbitTemplate batchRabbitTemplate(ConnectionFactory connectionFactory) {\n        // 创建 BatchingStrategy 对象，代表批量策略\n        int batchSize = 16384; // 超过收集的消息数量的最大条数。\n        int bufferLimit = 33554432; // 每次批量发送消息的最大内存\n        int timeout = 30000; // 超过收集的时间的最大等待时长，单位：毫秒\n        BatchingStrategy batchingStrategy = new SimpleBatchingStrategy(batchSize, bufferLimit, timeout);\n\n        // 创建 TaskScheduler 对象，用于实现超时发送的定时器\n        TaskScheduler taskScheduler = new ConcurrentTaskScheduler();\n\n        // 创建 BatchingRabbitTemplate 对象\n        BatchingRabbitTemplate batchTemplate = new BatchingRabbitTemplate(batchingStrategy, taskScheduler);\n        batchTemplate.setConnectionFactory(connectionFactory);\n        return batchTemplate;\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo05Consumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo05Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(queues = Demo05Message.QUEUE)\npublic class Demo05Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(Demo05Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo05Message.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo05Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_05\";\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_05\";\n\n    public static final String ROUTING_KEY = \"ROUTING_KEY_05\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo05Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo05Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo05Producer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo05Message;\nimport org.springframework.amqp.rabbit.core.BatchingRabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo05Producer {\n\n    @Autowired\n    private BatchingRabbitTemplate batchingRabbitTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 Demo05Message 消息\n        Demo05Message message = new Demo05Message();\n        message.setId(id);\n        // 同步发送消息\n        batchingRabbitTemplate.convertAndSend(Demo05Message.EXCHANGE, Demo05Message.ROUTING_KEY, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch/src/main/resources/application.yaml",
    "content": "spring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo05ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo05ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo05Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        for (int i = 0; i < 3; i++) {\n            // 同步发送消息\n            int id = (int) (System.currentTimeMillis() / 1000);\n            producer.syncSend(id);\n\n            // 故意每条消息之间，隔离 10 秒\n            logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n            Thread.sleep(10 * 1000L);\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-04-rabbitmq-demo-batch-consume</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RabbitMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.scheduling.annotation.EnableAsync;\n\n@SpringBootApplication\n@EnableAsync // 开启异步\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.config;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo05Message;\nimport org.springframework.amqp.core.Binding;\nimport org.springframework.amqp.core.BindingBuilder;\nimport org.springframework.amqp.core.DirectExchange;\nimport org.springframework.amqp.core.Queue;\nimport org.springframework.amqp.rabbit.batch.BatchingStrategy;\nimport org.springframework.amqp.rabbit.batch.SimpleBatchingStrategy;\nimport org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;\nimport org.springframework.amqp.rabbit.connection.ConnectionFactory;\nimport org.springframework.amqp.rabbit.core.BatchingRabbitTemplate;\nimport org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.scheduling.TaskScheduler;\nimport org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;\n\n@Configuration\npublic class RabbitConfig {\n\n    /**\n     * Direct Exchange 示例的配置类\n     */\n    public static class DirectExchangeDemoConfiguration {\n\n        // 创建 Queue\n        @Bean\n        public Queue demo05Queue() {\n            return new Queue(Demo05Message.QUEUE, // Queue 名字\n                    true, // durable: 是否持久化\n                    false, // exclusive: 是否排它\n                    false); // autoDelete: 是否自动删除\n        }\n\n        // 创建 Direct Exchange\n        @Bean\n        public DirectExchange demo05Exchange() {\n            return new DirectExchange(Demo05Message.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n        // 创建 Binding\n        // Exchange：Demo05Message.EXCHANGE\n        // Routing key：Demo05Message.ROUTING_KEY\n        // Queue：Demo05Message.QUEUE\n        @Bean\n        public Binding demo05Binding() {\n            return BindingBuilder.bind(demo05Queue()).to(demo05Exchange()).with(Demo05Message.ROUTING_KEY);\n        }\n\n    }\n\n    @Bean\n    public BatchingRabbitTemplate batchRabbitTemplate(ConnectionFactory connectionFactory) {\n        // 创建 BatchingStrategy 对象，代表批量策略\n        int batchSize = 16384; // 超过收集的消息数量的最大条数。\n        int bufferLimit = 33554432; // 每次批量发送消息的最大内存\n        int timeout = 30000; // 超过收集的时间的最大等待时长，单位：毫秒\n        BatchingStrategy batchingStrategy = new SimpleBatchingStrategy(batchSize, bufferLimit, timeout);\n\n        // 创建 TaskScheduler 对象，用于实现超时发送的定时器\n        TaskScheduler taskScheduler = new ConcurrentTaskScheduler();\n\n        // 创建 BatchingRabbitTemplate 对象\n        BatchingRabbitTemplate batchTemplate = new BatchingRabbitTemplate(batchingStrategy, taskScheduler);\n        batchTemplate.setConnectionFactory(connectionFactory);\n        return batchTemplate;\n    }\n\n    @Bean(name = \"consumerBatchContainerFactory\")\n    public SimpleRabbitListenerContainerFactory consumerBatchContainerFactory(\n            SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {\n        // 创建 SimpleRabbitListenerContainerFactory 对象\n        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();\n        configurer.configure(factory, connectionFactory);\n        // 额外添加批量消费的属性\n        factory.setBatchListener(true);\n        return factory;\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo05Consumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo05Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\n\n@Component\n@RabbitListener(queues = Demo05Message.QUEUE,\n    containerFactory = \"consumerBatchContainerFactory\")\npublic class Demo05Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(List<Demo05Message> messages) {\n        logger.info(\"[onMessage][线程编号:{} 消息数量：{}]\", Thread.currentThread().getId(), messages.size());\n    }\n\n//    @RabbitHandler(isDefault = true)\n//    public void onMessageX(List<Message> messages) {\n//        logger.info(\"[onMessage][线程编号:{} 消息数量：{}]\", Thread.currentThread().getId(), messages.size());\n//    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo05Message.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo05Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_05\";\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_05\";\n\n    public static final String ROUTING_KEY = \"ROUTING_KEY_05\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo05Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo05Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo06Producer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo05Message;\nimport org.springframework.amqp.rabbit.core.BatchingRabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo06Producer {\n\n    @Autowired\n    private BatchingRabbitTemplate batchingRabbitTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 Demo05Message 消息\n        Demo05Message message = new Demo05Message();\n        message.setId(id);\n        // 同步发送消息\n        batchingRabbitTemplate.convertAndSend(Demo05Message.EXCHANGE, Demo05Message.ROUTING_KEY, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume/src/main/resources/application.yaml",
    "content": "spring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo05ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo05ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo06Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        for (int i = 0; i < 3; i++) {\n            // 同步发送消息\n            int id = (int) (System.currentTimeMillis() / 1000);\n            producer.syncSend(id);\n\n            // 故意每条消息之间，隔离 10 秒\n            logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n            Thread.sleep(10 * 1000L);\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume-02/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-04-rabbitmq-demo-batch-consume-02</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RabbitMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume-02/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.scheduling.annotation.EnableAsync;\n\n@SpringBootApplication\n@EnableAsync // 开启异步\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume-02/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.config;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo06Message;\nimport org.springframework.amqp.core.Binding;\nimport org.springframework.amqp.core.BindingBuilder;\nimport org.springframework.amqp.core.DirectExchange;\nimport org.springframework.amqp.core.Queue;\nimport org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;\nimport org.springframework.amqp.rabbit.connection.ConnectionFactory;\nimport org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class RabbitConfig {\n\n    /**\n     * Direct Exchange 示例的配置类\n     */\n    public static class DirectExchangeDemoConfiguration {\n\n        // 创建 Queue\n        @Bean\n        public Queue demo06Queue() {\n            return new Queue(Demo06Message.QUEUE, // Queue 名字\n                    true, // durable: 是否持久化\n                    false, // exclusive: 是否排它\n                    false); // autoDelete: 是否自动删除\n        }\n\n        // 创建 Direct Exchange\n        @Bean\n        public DirectExchange demo06Exchange() {\n            return new DirectExchange(Demo06Message.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n        // 创建 Binding\n        // Exchange：Demo06Message.EXCHANGE\n        // Routing key：Demo06Message.ROUTING_KEY\n        // Queue：Demo06Message.QUEUE\n        @Bean\n        public Binding demo06Binding() {\n            return BindingBuilder.bind(demo06Queue()).to(demo06Exchange()).with(Demo06Message.ROUTING_KEY);\n        }\n\n    }\n\n    @Bean(name = \"consumerBatchContainerFactory\")\n    public SimpleRabbitListenerContainerFactory consumerBatchContainerFactory(\n            SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {\n        // 创建 SimpleRabbitListenerContainerFactory 对象\n        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();\n        configurer.configure(factory, connectionFactory);\n        // 额外添加批量消费的属性\n        factory.setBatchListener(true);\n        factory.setBatchSize(10);\n        factory.setReceiveTimeout(30 * 1000L);\n        factory.setConsumerBatchEnabled(true);\n        return factory;\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume-02/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo06Consumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo06Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\n\n@Component\n@RabbitListener(queues = Demo06Message.QUEUE,\n    containerFactory = \"consumerBatchContainerFactory\")\npublic class Demo06Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(List<Demo06Message> messages) {\n        logger.info(\"[onMessage][线程编号:{} 消息数量：{}]\", Thread.currentThread().getId(), messages.size());\n    }\n\n//    @RabbitHandler(isDefault = true)\n//    public void onMessageX(List<Message> messages) {\n//        logger.info(\"[onMessage][线程编号:{} 消息数量：{}]\", Thread.currentThread().getId(), messages.size());\n//    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume-02/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo06Message.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo06Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_06\";\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_06\";\n\n    public static final String ROUTING_KEY = \"ROUTING_KEY_06\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo06Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo06Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume-02/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo06Producer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo06Message;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo06Producer {\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 Demo06Message 消息\n        Demo06Message message = new Demo06Message();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(Demo06Message.EXCHANGE, Demo06Message.ROUTING_KEY, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume-02/src/main/resources/application.yaml",
    "content": "spring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume-02/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-batch-consume-02/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo06ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo06ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo06Producer producer;\n\n    @Test\n    public void testSyncSend01() throws InterruptedException {\n        // 发送 3 条消息\n        this.testSyncSend(3);\n    }\n\n    @Test\n    public void testSyncSen02() throws InterruptedException {\n        // 发送 10 条消息\n        this.testSyncSend(10);\n    }\n\n    private void testSyncSend(int n) throws InterruptedException {\n        for (int i = 0; i < n; i++) {\n            // 同步发送消息\n            int id = (int) (System.currentTimeMillis() / 1000);\n            producer.syncSend(id);\n            logger.info(\"[testSyncSendMore][发送编号：[{}] 发送成功]\", id);\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-concurrency/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-04-rabbitmq-demo-concurrency</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RabbitMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-concurrency/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.scheduling.annotation.EnableAsync;\n\n@SpringBootApplication\n@EnableAsync // 开启异步\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-concurrency/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.config;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo09Message;\nimport org.springframework.amqp.core.Binding;\nimport org.springframework.amqp.core.BindingBuilder;\nimport org.springframework.amqp.core.DirectExchange;\nimport org.springframework.amqp.core.Queue;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class RabbitConfig {\n\n    /**\n     * Direct Exchange 示例的配置类\n     */\n    public static class DirectExchangeDemoConfiguration {\n\n        // 创建 Queue\n        @Bean\n        public Queue demo09Queue() {\n            return new Queue(Demo09Message.QUEUE, // Queue 名字\n                    true, // durable: 是否持久化\n                    false, // exclusive: 是否排它\n                    false); // autoDelete: 是否自动删除\n        }\n\n        // 创建 Direct Exchange\n        @Bean\n        public DirectExchange demo09Exchange() {\n            return new DirectExchange(Demo09Message.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n        // 创建 Binding\n        // Exchange：Demo09Message.EXCHANGE\n        // Routing key：Demo09Message.ROUTING_KEY\n        // Queue：Demo09Message.QUEUE\n        @Bean\n        public Binding demo09Binding() {\n            return BindingBuilder.bind(demo09Queue()).to(demo09Exchange()).with(Demo09Message.ROUTING_KEY);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-concurrency/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo09Consumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo09Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\n@Component\n//@RabbitListener(queues = Demo09Message.QUEUE)\n@RabbitListener(queues = Demo09Message.QUEUE,\n    concurrency = \"2\")\n//@RabbitListener(queues = {\"QUEUE_DEMO_01\", \"QUEUE_DEMO_02\"})\npublic class Demo09Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(Demo09Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-concurrency/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo09Message.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo09Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_09\";\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_09\";\n\n    public static final String ROUTING_KEY = \"ROUTING_KEY_09\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo09Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo09Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-concurrency/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo09Producer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo09Message;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo09Producer {\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 Demo09Message 消息\n        Demo09Message message = new Demo09Message();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(Demo09Message.EXCHANGE, Demo09Message.ROUTING_KEY, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-concurrency/src/main/resources/application.yaml",
    "content": "spring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n    listener:\n      type: simple # 选择的 ListenerContainer 的类型。默认为 direct 类型\n      simple:\n        concurrency: 2 # 每个 @ListenerContainer 的并发消费的线程数\n        max-concurrency: 10 # 每个 @ListenerCon 允许的并发消费的线程数\n#      direct:\n#        consumers-per-queue: 2 # 对于每一个 @RabbitListener ，一个 Queue ，对应创建几个 Consumer 。\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-concurrency/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-concurrency/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo09ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo09ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo09Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        for (int i = 0; i < 10; i++) {\n            int id = (int) (System.currentTimeMillis() / 1000);\n            producer.syncSend(id);\n//            logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-04-rabbitmq-demo-confirm</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RabbitMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.config;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo13Message;\nimport org.springframework.amqp.core.Binding;\nimport org.springframework.amqp.core.BindingBuilder;\nimport org.springframework.amqp.core.DirectExchange;\nimport org.springframework.amqp.core.Queue;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class RabbitConfig {\n\n    /**\n     * Direct Exchange 示例的配置类\n     */\n    public static class DirectExchangeDemoConfiguration {\n\n        // 创建 Queue\n        @Bean\n        public Queue demo13Queue() {\n            return new Queue(Demo13Message.QUEUE, // Queue 名字\n                    true, // durable: 是否持久化\n                    false, // exclusive: 是否排它\n                    false); // autoDelete: 是否自动删除\n        }\n\n        // 创建 Direct Exchange\n        @Bean\n        public DirectExchange demo13Exchange() {\n            return new DirectExchange(Demo13Message.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n        // 创建 Binding\n        // Exchange：Demo13Message.EXCHANGE\n        // Routing key：Demo13Message.ROUTING_KEY\n        // Queue：Demo13Message.QUEUE\n        @Bean\n        public Binding demo13Binding() {\n            return BindingBuilder.bind(demo13Queue()).to(demo13Exchange()).with(Demo13Message.ROUTING_KEY);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo13Consumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo13Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(queues = Demo13Message.QUEUE)\npublic class Demo13Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(Demo13Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo13Message.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo13Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_13\";\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_13\";\n\n    public static final String ROUTING_KEY = \"ROUTING_KEY_13\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo13Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo13Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo13Producer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo13Message;\nimport com.rabbitmq.client.ConfirmCallback;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.core.RabbitOperations;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport java.io.IOException;\n\n@Component\npublic class Demo13Producer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 Demo13Message 消息\n        Demo13Message message = new Demo13Message();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.invoke(new RabbitOperations.OperationsCallback<Object>() {\n\n            @Override\n            public Object doInRabbit(RabbitOperations operations) {\n                // 同步发送消息\n                operations.convertAndSend(Demo13Message.EXCHANGE, Demo13Message.ROUTING_KEY, message);\n                logger.info(\"[doInRabbit][发送消息完成]\");\n                // 等待确认\n                operations.waitForConfirms(0); // timeout 参数，如果传递 0 ，表示无限等待\n                logger.info(\"[doInRabbit][等待 Confirm 完成]\");\n                return null;\n            }\n\n        }, new ConfirmCallback() {\n\n            @Override\n            public void handle(long deliveryTag, boolean multiple) throws IOException {\n                logger.info(\"[handle][Confirm 成功]\");\n            }\n\n        }, new ConfirmCallback() {\n\n            @Override\n            public void handle(long deliveryTag, boolean multiple) throws IOException {\n                logger.info(\"[handle][Confirm 失败]\");\n            }\n\n        });\n\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm/src/main/resources/application.yaml",
    "content": "spring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n    publisher-confirm-type: simple # 设置 Confirm 类型为 SIMPLE 。\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo13ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo13ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo13Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSend(id);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-04-rabbitmq-demo-confirm-async</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RabbitMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.config;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo13Message;\nimport org.springframework.amqp.core.Binding;\nimport org.springframework.amqp.core.BindingBuilder;\nimport org.springframework.amqp.core.DirectExchange;\nimport org.springframework.amqp.core.Queue;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class RabbitConfig {\n\n    /**\n     * Direct Exchange 示例的配置类\n     */\n    public static class DirectExchangeDemoConfiguration {\n\n        // 创建 Queue\n        @Bean\n        public Queue demo13Queue() {\n            return new Queue(Demo13Message.QUEUE, // Queue 名字\n                    true, // durable: 是否持久化\n                    false, // exclusive: 是否排它\n                    false); // autoDelete: 是否自动删除\n        }\n\n        // 创建 Direct Exchange\n        @Bean\n        public DirectExchange demo13Exchange() {\n            return new DirectExchange(Demo13Message.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n        // 创建 Binding\n        // Exchange：Demo13Message.EXCHANGE\n        // Routing key：Demo13Message.ROUTING_KEY\n        // Queue：Demo13Message.QUEUE\n        @Bean\n        public Binding demo13Binding() {\n            return BindingBuilder.bind(demo13Queue()).to(demo13Exchange()).with(Demo13Message.ROUTING_KEY);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo13Consumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo13Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(queues = Demo13Message.QUEUE)\npublic class Demo13Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(Demo13Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/core/RabbitProducerConfirmCallback.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.core;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.connection.CorrelationData;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class RabbitProducerConfirmCallback implements RabbitTemplate.ConfirmCallback {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    public RabbitProducerConfirmCallback(RabbitTemplate rabbitTemplate) {\n        rabbitTemplate.setConfirmCallback(this);\n    }\n\n    @Override\n    public void confirm(CorrelationData correlationData, boolean ack, String cause) {\n        if (ack) {\n            logger.info(\"[confirm][Confirm 成功 correlationData: {}]\", correlationData);\n        } else {\n            logger.error(\"[confirm][Confirm 失败 correlationData: {} cause: {}]\", correlationData, cause);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/core/RabbitProducerReturnCallback.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.core;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.core.Message;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class RabbitProducerReturnCallback implements RabbitTemplate.ReturnCallback {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    public RabbitProducerReturnCallback(RabbitTemplate rabbitTemplate) {\n        rabbitTemplate.setReturnCallback(this);\n    }\n\n    @Override\n    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {\n        logger.error(\"[returnedMessage][message: [{}] replyCode: [{}] replyText: [{}] exchange: [{}] routingKey: [{}]]\",\n                message, replyCode, replyText, exchange, routingKey);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo13Message.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo13Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_13\";\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_13\";\n\n    public static final String ROUTING_KEY = \"ROUTING_KEY_13\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo13Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo13Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo13Producer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo13Message;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo13Producer {\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 Demo13Message 消息\n        Demo13Message message = new Demo13Message();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(Demo13Message.EXCHANGE, Demo13Message.ROUTING_KEY, message);\n    }\n\n    public void syncSendReturn(Integer id) {\n        // 创建 Demo13Message 消息\n        Demo13Message message = new Demo13Message();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(Demo13Message.EXCHANGE, \"error\", message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/main/resources/application.yaml",
    "content": "spring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n    publisher-confirm-type: correlated # 设置 Confirm 类型为 CORRELATED 。\n    publisher-returns: true #\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-confirm-async/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo13ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo13ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo13Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSend(id);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testSyncSendReturn() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSendReturn(id);\n        logger.info(\"[testSyncSendReturn][发送编号：[{}] 发送成功]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-delay/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-04-rabbitmq-demo-delay</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RabbitMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-delay/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-delay/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.config;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo08Message;\nimport org.springframework.amqp.core.*;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class RabbitConfig {\n\n    /**\n     * Direct Exchange 示例的配置类\n     */\n    public static class DirectExchangeDemoConfiguration {\n\n        // 创建 Queue\n        @Bean\n        public Queue demo08Queue() {\n            return QueueBuilder.durable(Demo08Message.QUEUE) // durable: 是否持久化\n                    .exclusive() // exclusive: 是否排它\n                    .autoDelete() // autoDelete: 是否自动删除\n                    .ttl(10 * 1000) // 设置队列里的默认过期时间为 10 秒\n                    .deadLetterExchange(Demo08Message.EXCHANGE)\n                    .deadLetterRoutingKey(Demo08Message.DELAY_ROUTING_KEY)\n                    .build();\n        }\n\n        // 创建 Delay Queue\n        @Bean\n        public Queue demo08DelayQueue() {\n            return new Queue(Demo08Message.DELAY_QUEUE, // Queue 名字\n                    true, // durable: 是否持久化\n                    false, // exclusive: 是否排它\n                    false); // autoDelete: 是否自动删除\n        }\n\n        // 创建 Direct Exchange\n        @Bean\n        public DirectExchange demo08Exchange() {\n            return new DirectExchange(Demo08Message.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n        // 创建 Binding\n        // Exchange：Demo08Message.EXCHANGE\n        // Routing key：Demo08Message.ROUTING_KEY\n        // Queue：Demo08Message.QUEUE\n        @Bean\n        public Binding demo08Binding() {\n            return BindingBuilder.bind(demo08Queue()).to(demo08Exchange()).with(Demo08Message.ROUTING_KEY);\n        }\n\n        // 创建 Delay Binding\n        // Exchange：Demo08Message.EXCHANGE\n        // Routing key：Demo08Message.DELAY_ROUTING_KEY\n        // Queue：Demo08Message.DELAY_QUEUE\n        @Bean\n        public Binding demo08DelayBinding() {\n            return BindingBuilder.bind(demo08DelayQueue()).to(demo08Exchange()).with(Demo08Message.DELAY_ROUTING_KEY);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-delay/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo08Consumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo08Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(queues = Demo08Message.DELAY_QUEUE)\npublic class Demo08Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(Demo08Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-delay/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo08Message.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo08Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_08\"; // 正常队列\n    public static final String DELAY_QUEUE = \"DELAY_QUEUE_DEMO_08\"; // 延迟队列（死信队列）\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_08\";\n\n    public static final String ROUTING_KEY = \"ROUTING_KEY_08\"; // 正常路由键\n    public static final String DELAY_ROUTING_KEY = \"DELAY_ROUTING_KEY_08\"; // 延迟路由键（死信路由键）\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo08Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo08Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-delay/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo08Producer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo08Message;\nimport org.springframework.amqp.AmqpException;\nimport org.springframework.amqp.core.Message;\nimport org.springframework.amqp.core.MessagePostProcessor;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo08Producer {\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public void syncSend(Integer id, Integer delay) {\n        // 创建 Demo07Message 消息\n        Demo08Message message = new Demo08Message();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(Demo08Message.EXCHANGE, Demo08Message.ROUTING_KEY, message, new MessagePostProcessor() {\n\n            @Override\n            public Message postProcessMessage(Message message) throws AmqpException {\n                // 设置消息的 TTL 过期时间\n                if (delay != null && delay > 0) {\n                    message.getMessageProperties().setExpiration(String.valueOf(delay)); // Spring-AMQP API 设计有问题，所以传入了 String = =\n                }\n                return message;\n            }\n\n        });\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-delay/src/main/resources/application.yaml",
    "content": "spring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-delay/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-delay/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo08ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo08ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo08Producer producer;\n\n    @Test\n    public void testSyncSend01() throws InterruptedException {\n        // 不设置消息的过期时间，使用队列默认的消息过期时间\n        this.testSyncSendDelay(null);\n    }\n\n    @Test\n    public void testSyncSend02() throws InterruptedException {\n        // 设置发送消息的过期时间为 5000 毫秒\n        this.testSyncSendDelay(5000);\n    }\n\n    private void testSyncSendDelay(Integer delay) throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSend(id, delay);\n        logger.info(\"[testSyncSendDelay][发送编号：[{}] 发送成功]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-04-rabbitmq-demo-error-handler</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RabbitMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.config;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo16Message;\nimport org.springframework.amqp.core.Binding;\nimport org.springframework.amqp.core.BindingBuilder;\nimport org.springframework.amqp.core.DirectExchange;\nimport org.springframework.amqp.core.Queue;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class RabbitConfig {\n\n    /**\n     * Direct Exchange 示例的配置类\n     */\n    public static class DirectExchangeDemoConfiguration {\n\n        // 创建 Queue\n        @Bean\n        public Queue demo16Queue() {\n            return new Queue(Demo16Message.QUEUE, // Queue 名字\n                    true, // durable: 是否持久化\n                    false, // exclusive: 是否排它\n                    false); // autoDelete: 是否自动删除\n        }\n\n        // 创建 Direct Exchange\n        @Bean\n        public DirectExchange demo16Exchange() {\n            return new DirectExchange(Demo16Message.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n        // 创建 Binding\n        // Exchange：Demo16Message.EXCHANGE\n        // Routing key：Demo16Message.ROUTING_KEY\n        // Queue：Demo16Message.QUEUE\n        @Bean\n        public Binding demo16Binding() {\n            return BindingBuilder.bind(demo16Queue()).to(demo16Exchange()).with(Demo16Message.ROUTING_KEY);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo16Consumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo16Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(queues = Demo16Message.QUEUE,\n    errorHandler = \"rabbitListenerErrorHandler\")\n//@RabbitListener(queues = Demo15Message.QUEUE)\npublic class Demo16Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(Demo16Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n        // 模拟消费异常\n        throw new RuntimeException(\"你猜\");\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/core/RabbitListenerErrorHandlerImpl.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.core;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.core.Message;\nimport org.springframework.amqp.rabbit.listener.api.RabbitListenerErrorHandler;\nimport org.springframework.amqp.rabbit.support.ListenerExecutionFailedException;\nimport org.springframework.stereotype.Component;\n\n@Component(\"rabbitListenerErrorHandler\")\npublic class RabbitListenerErrorHandlerImpl implements RabbitListenerErrorHandler {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public Object handleError(Message amqpMessage, org.springframework.messaging.Message<?> message,\n                              ListenerExecutionFailedException exception) {\n        // 打印异常日志\n        logger.error(\"[handleError][amqpMessage:[{}] message:[{}]]\", amqpMessage, message, exception);\n\n        // 直接继续抛出异常\n        throw exception;\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/core/RabbitLoggingErrorHandler.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.core;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.ErrorHandler;\n\n@Component\npublic class RabbitLoggingErrorHandler implements ErrorHandler {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    public RabbitLoggingErrorHandler(SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory) {\n        rabbitListenerContainerFactory.setErrorHandler(this);\n    }\n\n    @Override\n    public void handleError(Throwable t) {\n        logger.error(\"[handleError][发生异常]]\", t);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo16Message.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo16Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_16\";\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_16\";\n\n    public static final String ROUTING_KEY = \"ROUTING_KEY_16\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo16Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo16Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo16Producer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo16Message;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo16Producer {\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 Demo16Message 消息\n        Demo16Message message = new Demo16Message();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(Demo16Message.EXCHANGE, Demo16Message.ROUTING_KEY, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/main/resources/application.yaml",
    "content": "spring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-error-handler/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo16ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo16ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo16Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSend(id);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-json/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-04-rabbitmq-demo-json</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RabbitMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!-- Jackson 依赖  -->\n        <dependency>\n            <groupId>com.fasterxml.jackson.core</groupId>\n            <artifactId>jackson-databind</artifactId>\n            <version>2.9.10.1</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-json/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-json/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.config;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo15Message;\nimport org.springframework.amqp.core.Binding;\nimport org.springframework.amqp.core.BindingBuilder;\nimport org.springframework.amqp.core.DirectExchange;\nimport org.springframework.amqp.core.Queue;\nimport org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;\nimport org.springframework.amqp.support.converter.MessageConverter;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class RabbitConfig {\n\n    /**\n     * Direct Exchange 示例的配置类\n     */\n    public static class DirectExchangeDemoConfiguration {\n\n        // 创建 Queue\n        @Bean\n        public Queue demo15Queue() {\n            return new Queue(Demo15Message.QUEUE, // Queue 名字\n                    true, // durable: 是否持久化\n                    false, // exclusive: 是否排它\n                    false); // autoDelete: 是否自动删除\n        }\n\n        // 创建 Direct Exchange\n        @Bean\n        public DirectExchange demo15Exchange() {\n            return new DirectExchange(Demo15Message.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n        // 创建 Binding\n        // Exchange：Demo15Message.EXCHANGE\n        // Routing key：Demo15Message.ROUTING_KEY\n        // Queue：Demo15Message.QUEUE\n        @Bean\n        public Binding demo15Binding() {\n            return BindingBuilder.bind(demo15Queue()).to(demo15Exchange()).with(Demo15Message.ROUTING_KEY);\n        }\n\n    }\n\n    @Bean\n    public MessageConverter messageConverter() {\n        return new Jackson2JsonMessageConverter();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-json/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo15Consumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo15Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.core.Message;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(queues = Demo15Message.QUEUE)\npublic class Demo15Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler(isDefault = true)\n    public void onMessage(Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(),\n                new String(message.getBody()));\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-json/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo15Message.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo15Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_15\";\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_15\";\n\n    public static final String ROUTING_KEY = \"ROUTING_KEY_15\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo15Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo15Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-json/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo15Producer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo15Message;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo15Producer {\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 Demo01Message 消息\n        Demo15Message message = new Demo15Message();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(Demo15Message.EXCHANGE, Demo15Message.ROUTING_KEY, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-json/src/main/resources/application.yaml",
    "content": "spring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-json/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-json/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo15ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo15ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo15Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSend(id);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-04-rabbitmq-demo-message-model</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RabbitMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.config;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.BroadcastMessage;\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.ClusteringMessage;\nimport org.springframework.amqp.core.TopicExchange;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class RabbitConfig {\n\n    /**\n     * 广播消费的示例的配置\n     */\n    public static class BroadcastingConfiguration {\n\n        // 创建 Topic Exchange\n        @Bean\n        public TopicExchange broadcastingExchange() {\n            return new TopicExchange(BroadcastMessage.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n    }\n\n    /**\n     * 集群消费的示例的配置\n     */\n    public static class ClusteringConfiguration {\n\n        // 创建 Topic Exchange\n        @Bean\n        public TopicExchange clusteringExchange() {\n            return new TopicExchange(ClusteringMessage.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/BroadcastConsumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.BroadcastMessage;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.core.ExchangeTypes;\nimport org.springframework.amqp.rabbit.annotation.*;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(\n        bindings = @QueueBinding(\n                value = @Queue(\n                        name = BroadcastMessage.QUEUE + \"-\" + \"#{T(java.util.UUID).randomUUID()}\",\n                        autoDelete = \"true\"\n                ),\n                exchange = @Exchange(\n                        name = BroadcastMessage.EXCHANGE,\n                        type = ExchangeTypes.TOPIC,\n                        declare = \"false\"\n                )\n        )\n)\npublic class BroadcastConsumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(BroadcastMessage message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/ClusteringConsumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.ClusteringMessage;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.core.ExchangeTypes;\nimport org.springframework.amqp.rabbit.annotation.*;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(\n        bindings = @QueueBinding(\n                value = @Queue(\n                        name = ClusteringMessage.QUEUE + \"-\" + \"GROUP-01\"\n                ),\n                exchange = @Exchange(\n                        name = ClusteringMessage.EXCHANGE,\n                        type = ExchangeTypes.TOPIC,\n                        declare = \"false\"\n                ),\n                key = \"#\"\n        )\n)\npublic class ClusteringConsumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(ClusteringMessage message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/BroadcastMessage.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\n/**\n * 广播消费的消息示例\n */\npublic class BroadcastMessage implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_BROADCASTING\";\n\n    public static final String EXCHANGE = \"EXCHANGE_BROADCASTING\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public BroadcastMessage setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"BroadcastMessage{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/ClusteringMessage.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\n/**\n * 广播消费的消息示例\n */\npublic class ClusteringMessage implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_CLUSTERING\";\n\n    public static final String EXCHANGE = \"EXCHANGE_CLUSTERING\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public ClusteringMessage setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusteringtMessage{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/BroadcastProducer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.BroadcastMessage;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class BroadcastProducer {\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 BroadcastMessage 消息\n        BroadcastMessage message = new BroadcastMessage();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(BroadcastMessage.EXCHANGE, null, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/ClusteringProducer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.ClusteringMessage;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ClusteringProducer {\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 ClusteringMessage 消息\n        ClusteringMessage message = new ClusteringMessage();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(ClusteringMessage.EXCHANGE, null, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/main/resources/application.yaml",
    "content": "spring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n    template:\n      # 对应 RabbitProperties.Retry 类\n      retry:\n        enabled: true # 开启发送机制\n        max-attempts: 3 # 最大重试次数。默认为 3 。\n        initial-interval: 1000 # 重试间隔，单位为毫秒。默认为 1000 。\n    listener:\n      simple:\n        # 对应 RabbitProperties.ListenerRetry 类\n        retry:\n          enabled: true # 开启消费重试机制\n          max-attempts: 3 # 最大重试次数。默认为 3 。\n          initial-interval: 1000 # 重试间隔，单位为毫秒。默认为 1000 。\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/BroadcastProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class BroadcastProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private BroadcastProducer producer;\n\n    @Test\n    public void mock() throws InterruptedException {\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        for (int i = 0; i < 3; i++) {\n            int id = (int) (System.currentTimeMillis() / 1000);\n            producer.syncSend(id);\n            logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-message-model/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/ClusteringProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class ClusteringProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private ClusteringProducer producer;\n\n    @Test\n    public void mock() throws InterruptedException {\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        // 发送 3 条消息\n        for (int i = 0; i < 3; i++) {\n            int id = (int) (System.currentTimeMillis() / 1000);\n            producer.syncSend(id);\n            logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-orderly/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-04-rabbitmq-demo-orderly</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RabbitMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-orderly/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.scheduling.annotation.EnableAsync;\n\n/**\n * TODO RabbitMQ 顺序消息的示例，暂时未提供\n */\n@SpringBootApplication\n@EnableAsync // 开启异步\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-orderly/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.config;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo10Message;\nimport org.springframework.amqp.core.Binding;\nimport org.springframework.amqp.core.BindingBuilder;\nimport org.springframework.amqp.core.DirectExchange;\nimport org.springframework.amqp.core.Queue;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class RabbitConfig {\n\n    /**\n     * Direct Exchange 示例的配置类\n     */\n    public static class DirectExchangeDemoConfiguration {\n\n        // 创建 Queue\n        @Bean\n        public Queue demo10Queue0() {\n            return new Queue(Demo10Message.QUEUE_0);\n        }\n        @Bean\n        public Queue demo10Queue1() {\n            return new Queue(Demo10Message.QUEUE_1);\n        }\n        @Bean\n        public Queue demo10Queue2() {\n            return new Queue(Demo10Message.QUEUE_2);\n        }\n        @Bean\n        public Queue demo10Queue3() {\n            return new Queue(Demo10Message.QUEUE_3);\n        }\n\n        // 创建 Direct Exchange\n        @Bean\n        public DirectExchange demo10Exchange() {\n            return new DirectExchange(Demo10Message.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n        // 创建 Binding\n        @Bean\n        public Binding demo10Binding0() {\n            return BindingBuilder.bind(demo10Queue0()).to(demo10Exchange()).with(\"0\");\n        }\n        @Bean\n        public Binding demo10Binding1() {\n            return BindingBuilder.bind(demo10Queue1()).to(demo10Exchange()).with(\"1\");\n        }\n        @Bean\n        public Binding demo10Binding2() {\n            return BindingBuilder.bind(demo10Queue2()).to(demo10Exchange()).with(\"2\");\n        }\n        @Bean\n        public Binding demo10Binding3() {\n            return BindingBuilder.bind(demo10Queue3()).to(demo10Exchange()).with(\"3\");\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-orderly/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo10Consumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo10Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.messaging.Message;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(queues = Demo10Message.QUEUE_0)\n@RabbitListener(queues = Demo10Message.QUEUE_1)\n@RabbitListener(queues = Demo10Message.QUEUE_2)\n@RabbitListener(queues = Demo10Message.QUEUE_3)\npublic class Demo10Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler(isDefault = true)\n    public void onMessage(Message<Demo10Message> message) {\n        logger.info(\"[onMessage][线程编号:{} Queue:{} 消息编号：{}]\", Thread.currentThread().getId(), getQueue(message),\n                message.getPayload().getId());\n    }\n\n    private static String getQueue(Message<Demo10Message> message) {\n        return message.getHeaders().get(\"amqp_consumerQueue\", String.class);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-orderly/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo10Message.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo10Message implements Serializable {\n\n    private static final String QUEUE_BASE = \"QUEUE_DEMO_10-\";\n    public static final String QUEUE_0 = QUEUE_BASE + \"0\";\n    public static final String QUEUE_1 = QUEUE_BASE + \"1\";\n    public static final String QUEUE_2 = QUEUE_BASE + \"2\";\n    public static final String QUEUE_3 = QUEUE_BASE + \"3\";\n\n    public static final int QUEUE_COUNT = 4;\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_10\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo10Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo10Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-orderly/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo10Producer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo10Message;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo10Producer {\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 Demo10Message 消息\n        Demo10Message message = new Demo10Message();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(Demo10Message.EXCHANGE, this.getRoutingKey(id), message);\n    }\n\n    private String getRoutingKey(Integer id) {\n        return String.valueOf(id % Demo10Message.QUEUE_COUNT);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-orderly/src/main/resources/application.yaml",
    "content": "spring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-orderly/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-orderly/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo10ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo10ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo10Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        for (int i = 0; i < 2; i++) {\n            for (int id = 0; id < 4; id++) {\n                producer.syncSend(id);\n//            logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n            }\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-rpc/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-04-rabbitmq-demo-rpc</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RabbitMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-rpc/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.scheduling.annotation.EnableAsync;\n\n@SpringBootApplication\n@EnableAsync // 开启异步\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-rpc/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.config;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo14Message;\nimport org.springframework.amqp.core.Binding;\nimport org.springframework.amqp.core.BindingBuilder;\nimport org.springframework.amqp.core.DirectExchange;\nimport org.springframework.amqp.core.Queue;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class RabbitConfig {\n\n    /**\n     * Direct Exchange 示例的配置类\n     */\n    public static class DirectExchangeDemoConfiguration {\n\n        // 创建 Queue\n        @Bean\n        public Queue demo01Queue() {\n            return new Queue(Demo14Message.QUEUE, // Queue 名字\n                    false, // durable: 是否持久化\n                    false, // exclusive: 是否排它\n                    false); // autoDelete: 是否自动删除\n        }\n\n        // 创建 Direct Exchange\n        @Bean\n        public DirectExchange demo01Exchange() {\n            return new DirectExchange(Demo14Message.EXCHANGE,\n                    false,  // durable: 是否持久化\n                    false);  // autoDelete: 是否自动删除\n        }\n\n        // 创建 Binding\n        // Exchange：Demo01Message.EXCHANGE\n        // Routing key：Demo01Message.ROUTING_KEY\n        // Queue：Demo01Message.QUEUE\n        @Bean\n        public Binding demo01Binding() {\n            return BindingBuilder.bind(demo01Queue()).to(demo01Exchange()).with(Demo14Message.ROUTING_KEY);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-rpc/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo14Consumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo14Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(queues = Demo14Message.QUEUE)\npublic class Demo14Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public String onMessage(Demo14Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n        // 返回结果\n        return \"nicai\";\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-rpc/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo14Message.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo14Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_14\";\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_14\";\n\n    public static final String ROUTING_KEY = \"ROUTING_KEY_14\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo14Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo14Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-rpc/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo14Producer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo14Message;\nimport org.springframework.amqp.rabbit.connection.CorrelationData;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport java.util.UUID;\n\n@Component\npublic class Demo14Producer {\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public String syncSend(Integer id) {\n        // 创建 Demo01Message 消息\n        Demo14Message message = new Demo14Message();\n        message.setId(id);\n        // 创建 CorrelationData 对象\n        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());\n        // 同步发送消息，并接收结果\n        return (String) rabbitTemplate.convertSendAndReceive(Demo14Message.EXCHANGE, Demo14Message.ROUTING_KEY, message,\n                correlationData);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-rpc/src/main/resources/application.yaml",
    "content": "spring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-rpc/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-rpc/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo14ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo14ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo14Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        String result = producer.syncSend(id);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送成功 消费结果：[{}]]\", id, result);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n//    @Test\n//    public void testSyncSend2() throws InterruptedException {\n//        int id = (int) (System.currentTimeMillis() / 1000);\n//        String result = producer.syncSend(id);\n//        logger.info(\"[testSyncSend][发送编号：[{}] 发送成功 消费结果：[{}]]\", id, result);\n//\n//        // 阻塞等待，保证消费\n//        new CountDownLatch(1).await();\n//    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-transaction/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-04-rabbitmq-demo-transaction</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RabbitMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-transaction/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.transaction.annotation.EnableTransactionManagement;\n\n@SpringBootApplication\n@EnableTransactionManagement // 开启事务\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-transaction/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/config/RabbitConfig.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.config;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo11Message;\nimport org.springframework.amqp.core.Binding;\nimport org.springframework.amqp.core.BindingBuilder;\nimport org.springframework.amqp.core.DirectExchange;\nimport org.springframework.amqp.core.Queue;\nimport org.springframework.amqp.rabbit.connection.ConnectionFactory;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.amqp.rabbit.transaction.RabbitTransactionManager;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.transaction.annotation.EnableTransactionManagement;\n\n@Configuration\n@EnableTransactionManagement\npublic class RabbitConfig {\n\n    /**\n     * Direct Exchange 示例的配置类\n     */\n    public static class DirectExchangeDemoConfiguration {\n\n        // 创建 Queue\n        @Bean\n        public Queue demo11Queue() {\n            return new Queue(Demo11Message.QUEUE, // Queue 名字\n                    true, // durable: 是否持久化\n                    false, // exclusive: 是否排它\n                    false); // autoDelete: 是否自动删除\n        }\n\n        // 创建 Direct Exchange\n        @Bean\n        public DirectExchange demo11Exchange() {\n            return new DirectExchange(Demo11Message.EXCHANGE,\n                    true,  // durable: 是否持久化\n                    false);  // exclusive: 是否排它\n        }\n\n        // 创建 Binding\n        // Exchange：Demo11Message.EXCHANGE\n        // Routing key：Demo11Message.ROUTING_KEY\n        // Queue：Demo11Message.QUEUE\n        @Bean\n        public Binding demo11Binding() {\n            return BindingBuilder.bind(demo11Queue()).to(demo11Exchange()).with(Demo11Message.ROUTING_KEY);\n        }\n\n    }\n\n    @Bean\n    public RabbitTransactionManager rabbitTransactionManager(ConnectionFactory connectionFactory, RabbitTemplate rabbitTemplate) {\n        // 设置 RabbitTemplate 支持事务\n        rabbitTemplate.setChannelTransacted(true);\n\n        // 创建 RabbitTransactionManager 对象\n        return new RabbitTransactionManager(connectionFactory);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-transaction/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/consumer/Demo11Consumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo11Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(queues = Demo11Message.QUEUE)\npublic class Demo11Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(Demo11Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-transaction/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/message/Demo11Message.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo11Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_11\";\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_11\";\n\n    public static final String ROUTING_KEY = \"ROUTING_KEY_11\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo11Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo11Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-transaction/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo11Producer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.message.Demo11Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\nimport org.springframework.transaction.annotation.Transactional;\n\n@Component\npublic class Demo11Producer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    @Transactional\n    public void syncSend(Integer id) throws InterruptedException {\n        // 创建 Demo11Message 消息\n        Demo11Message message = new Demo11Message();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(Demo11Message.EXCHANGE, Demo11Message.ROUTING_KEY, message);\n        logger.info(\"[syncSend][发送编号：[{}] 发送成功]\", id);\n\n        // 等待\n        Thread.sleep(10 * 1000L);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-transaction/src/main/resources/application.yaml",
    "content": "spring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-transaction/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-demo-transaction/src/test/java/cn/iocoder/springboot/lab04/rabbitmqdemo/producer/Demo11ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo.producer;\n\nimport cn.iocoder.springboot.lab04.rabbitmqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo11ProducerTest {\n\n    @Autowired\n    private Demo11Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSend(id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-native/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-04-rabbitmq-native</artifactId>\n\n    <dependencies>\n        <!-- 引入 RabbitMQ 客户端依赖 -->\n        <dependency>\n            <groupId>com.rabbitmq</groupId>\n            <artifactId>amqp-client</artifactId>\n            <version>5.7.3</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-native/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/RabbitMQConsumer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n\nimport com.rabbitmq.client.*;\n\nimport java.io.IOException;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\n\npublic class RabbitMQConsumer {\n\n    public static void main(String[] args) throws IOException, TimeoutException {\n        // 创建连接\n        Connection connection = RabbitMQProducer.getConnection();\n\n        // 创建信道\n        final Channel channel = connection.createChannel();\n        channel.basicQos(64); // 设置客户端最多接收未被 ack 的消息数量为 64 。\n\n        // 创建消费者\n        Consumer consumer = new DefaultConsumer(channel) {\n\n            @Override\n            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {\n                // 打印日志\n                System.out.println(String.format(\"[线程：%s][路由键：%s][消息内容：%s]\",\n                        Thread.currentThread(), envelope.getRoutingKey(), new String(body)));\n                // ack 消息已经消费\n                channel.basicAck(envelope.getDeliveryTag(), false);\n            }\n\n        };\n        // 订阅消费 QUEUE_NAME 队列\n        channel.basicConsume(RabbitMQProducer.QUEUE_NAME, consumer);\n\n        // 关闭\n        try {\n            TimeUnit.HOURS.sleep(1);\n        } catch (InterruptedException ignore) {\n        }\n        channel.close();\n        connection.close();\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/lab-04-rabbitmq-native/src/main/java/cn/iocoder/springboot/lab04/rabbitmqdemo/RabbitMQProducer.java",
    "content": "package cn.iocoder.springboot.lab04.rabbitmqdemo;\n\nimport com.rabbitmq.client.Channel;\nimport com.rabbitmq.client.Connection;\nimport com.rabbitmq.client.ConnectionFactory;\nimport com.rabbitmq.client.MessageProperties;\n\nimport java.io.IOException;\nimport java.util.concurrent.TimeoutException;\n\npublic class RabbitMQProducer {\n\n    private static final String IP_ADDRESS = \"127.0.0.1\";\n    private static final Integer PORT = 5672;\n    private static final String USERNAME = \"guest\";\n    private static final String PASSWORD = \"guest\";\n\n    private static final String EXCHANGE_NAME = \"exchange_demo\";\n    private static final String ROUTING_KEY = \"routingkey_demo\";\n    public static final String QUEUE_NAME = \"queue_demo\"; // 只有 QUEUE_NAME 需要共享给 RabbitMQConsumer\n\n    public static void main(String[] args) throws IOException, TimeoutException {\n        // 创建连接\n        Connection connection = getConnection();\n\n        // 创建信道\n        Channel channel = connection.createChannel();\n\n        // 初始化测试用的 Exchange 和 Queue\n        initExchangeAndQueue(channel);\n\n        // 发送 3 条消息\n        for (int i = 0; i < 3; i++) {\n            String message = \"Hello World\" + i;\n            channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes());\n        }\n\n        // 关闭\n        channel.close();\n        connection.close();\n    }\n\n    public static Connection getConnection() throws IOException, TimeoutException {\n        ConnectionFactory factory = new ConnectionFactory();\n        factory.setHost(IP_ADDRESS);\n        factory.setPort(PORT);\n        factory.setUsername(USERNAME);\n        factory.setPassword(PASSWORD);\n        return factory.newConnection();\n    }\n\n    // 创建 RabbitMQ Exchange 和 Queue ，然后使用 ROUTING_KEY 路由键将两者绑定。\n    // 该步骤，其实可以在 RabbitMQ Management 上操作，并不一定需要在代码中\n    private static void initExchangeAndQueue(Channel channel) throws IOException {\n        // 创建交换器：direct、持久化、不自动删除\n        channel.exchangeDeclare(EXCHANGE_NAME, \"direct\", true, false, null);\n\n        // 创建队列：持久化、非排他、非自动删除的队列\n        channel.queueDeclare(QUEUE_NAME, true, false, false, null);\n\n        // 将交换器与队列通过路由键绑定\n        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY);\n    }\n\n}\n"
  },
  {
    "path": "lab-04-rabbitmq/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-04-rabbitmq</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-04-rabbitmq-native</module>\n        <module>lab-04-rabbitmq-demo</module>\n        <module>lab-04-rabbitmq-demo-batch</module>\n        <module>lab-04-rabbitmq-demo-batch-consume</module>\n        <module>lab-04-rabbitmq-demo-batch-consume-02</module>\n        <module>lab-04-rabbitmq-consume-retry</module>\n        <module>lab-04-rabbitmq-demo-delay</module>\n        <module>lab-04-rabbitmq-demo-message-model</module>\n        <module>lab-04-rabbitmq-demo-concurrency</module>\n        <module>lab-04-rabbitmq-demo-orderly</module>\n        <module>lab-04-rabbitmq-demo-transaction</module>\n        <module>lab-04-rabbitmq-demo-ack</module>\n        <module>lab-04-rabbitmq-demo-confirm</module>\n        <module>lab-04-rabbitmq-demo-confirm-async</module>\n        <module>lab-04-rabbitmq-demo-rpc</module>\n        <module>lab-04-rabbitmq-demo-json</module>\n        <module>lab-04-rabbitmq-demo-error-handler</module>\n    </modules>\n\n    <dependencies>\n        <dependency>\n            <groupId>com.rabbitmq</groupId>\n            <artifactId>amqp-client</artifactId>\n            <version>5.5.3</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-04-rabbitmq/《芋道 Spring Boot 消息队列 RabbitMQ 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/RabbitMQ/?github>\n"
  },
  {
    "path": "lab-05-benchmark-tomcat-jetty-undertow/lab-05-jetty/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-05-benchmark-tomcat-jetty-undertow</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-05-jetty</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n            <exclusions>\n                <exclusion>\n                    <groupId>org.springframework.boot</groupId>\n                    <artifactId>spring-boot-starter-tomcat</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jetty</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <!-- 提供给 mapstruct 使用 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <configuration>\n                    <fork>true</fork>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-05-benchmark-tomcat-jetty-undertow/lab-05-jetty/src/main/java/cn/iocoder/springboot/labs/lab05/tomcat/Controller.java",
    "content": "package cn.iocoder.springboot.labs.lab05.tomcat;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class Controller {\n\n    @GetMapping(\"/hello\")\n    public String hello() {\n        return \"world\";\n    }\n\n}\n"
  },
  {
    "path": "lab-05-benchmark-tomcat-jetty-undertow/lab-05-jetty/src/main/java/cn/iocoder/springboot/labs/lab05/tomcat/JettyApplication.java",
    "content": "package cn.iocoder.springboot.labs.lab05.tomcat;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class JettyApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(JettyApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "lab-05-benchmark-tomcat-jetty-undertow/lab-05-tomcat/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-05-benchmark-tomcat-jetty-undertow</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-05-tomcat</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <!-- 提供给 mapstruct 使用 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <configuration>\n                    <fork>true</fork>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-05-benchmark-tomcat-jetty-undertow/lab-05-tomcat/src/main/java/cn/iocoder/springboot/labs/lab05/tomcat/Controller.java",
    "content": "package cn.iocoder.springboot.labs.lab05.tomcat;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class Controller {\n\n    @GetMapping(\"/hello\")\n    public String hello() {\n        return \"world\";\n    }\n\n    @GetMapping(\"/sleep\")\n    public String sleep() throws InterruptedException {\n        Thread.sleep(100L);\n        return \"sleep\";\n    }\n\n}\n"
  },
  {
    "path": "lab-05-benchmark-tomcat-jetty-undertow/lab-05-tomcat/src/main/java/cn/iocoder/springboot/labs/lab05/tomcat/TomcatApplication.java",
    "content": "package cn.iocoder.springboot.labs.lab05.tomcat;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class TomcatApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TomcatApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "lab-05-benchmark-tomcat-jetty-undertow/lab-05-tomcat-apr/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-05-benchmark-tomcat-jetty-undertow</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n\n<!--    <parent>-->\n<!--        <groupId>org.springframework.boot</groupId>-->\n<!--        <artifactId>spring-boot-starter-parent</artifactId>-->\n<!--        <version>1.5.6.RELEASE</version>-->\n<!--        <relativePath/> &lt;!&ndash; lookup parent from repository &ndash;&gt;-->\n<!--    </parent>-->\n\n<!--    <parent>-->\n<!--        <groupId>org.springframework.boot</groupId>-->\n<!--        <artifactId>spring-boot-starter-parent</artifactId>-->\n<!--        <version>2.1.3.RELEASE</version>-->\n<!--        <relativePath/> &lt;!&ndash; lookup parent from repository &ndash;&gt;-->\n<!--    </parent>-->\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-05-tomcat-apr</artifactId>\n<!--    <version>1.0.0</version>-->\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n<!--        <dependency>-->\n<!--            <groupId>org.apache.tomcat.embed</groupId>-->\n<!--            <artifactId>tomcat-embed-core</artifactId>-->\n<!--            <version>9.0.20</version>-->\n<!--        </dependency>-->\n    </dependencies>\n\n    <build>\n        <plugins>\n            <!-- 提供给 mapstruct 使用 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <configuration>\n                    <fork>true</fork>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-05-benchmark-tomcat-jetty-undertow/lab-05-tomcat-apr/src/main/java/cn/iocoder/springboot/labs/lab05/tomcat/Controller.java",
    "content": "package cn.iocoder.springboot.labs.lab05.tomcat;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class Controller {\n\n    @GetMapping(\"/hello\")\n    public String hello() {\n        return \"world\";\n    }\n\n}\n"
  },
  {
    "path": "lab-05-benchmark-tomcat-jetty-undertow/lab-05-tomcat-apr/src/main/java/cn/iocoder/springboot/labs/lab05/tomcat/TomcatAprApplication.java",
    "content": "package cn.iocoder.springboot.labs.lab05.tomcat;\n\nimport org.apache.catalina.core.AprLifecycleListener;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;\nimport org.springframework.boot.web.servlet.server.ServletWebServerFactory;\nimport org.springframework.context.annotation.Bean;\n\n@SpringBootApplication\npublic class TomcatAprApplication {\n\n    @Bean\n    public ServletWebServerFactory servletWebServerFactory() {\n        TomcatServletWebServerFactory tomcatServletWebServerFactory = new TomcatServletWebServerFactory();\n        tomcatServletWebServerFactory.setProtocol(\"org.apache.coyote.http11.Http11AprProtocol\");\n        tomcatServletWebServerFactory.addContextLifecycleListeners(new AprLifecycleListener());\n        return tomcatServletWebServerFactory;\n    }\n\n    // Mac 下，VM 启动参数，需要增加 -Djava.library.path=/usr/local/opt/tomcat-native/lib 。\n    // 当然是在安装了 apr 的情况。安装命令是 brew install apr tomcat-native\n    public static void main(String[] args) {\n        SpringApplication.run(TomcatAprApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "lab-05-benchmark-tomcat-jetty-undertow/lab-05-undertow/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-05-benchmark-tomcat-jetty-undertow</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-05-undertow</artifactId>\n    <version>1.0.0</version>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n            <exclusions>\n                <exclusion>\n                    <groupId>org.springframework.boot</groupId>\n                    <artifactId>spring-boot-starter-tomcat</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-undertow</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <!-- 提供给 mapstruct 使用 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <configuration>\n                    <fork>true</fork>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-05-benchmark-tomcat-jetty-undertow/lab-05-undertow/src/main/java/cn/iocoder/springboot/labs/lab05/undertow/Controller.java",
    "content": "package cn.iocoder.springboot.labs.lab05.undertow;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class Controller {\n\n    @GetMapping(\"/hello\")\n    public String hello() {\n        return \"world\";\n    }\n\n}\n"
  },
  {
    "path": "lab-05-benchmark-tomcat-jetty-undertow/lab-05-undertow/src/main/java/cn/iocoder/springboot/labs/lab05/undertow/UndertowApplication.java",
    "content": "package cn.iocoder.springboot.labs.lab05.undertow;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UndertowApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UndertowApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "lab-05-benchmark-tomcat-jetty-undertow/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cn.iocoder.springboot.labs</groupId>\n    <artifactId>lab-05-benchmark-tomcat-jetty-undertow</artifactId>\n    <version>1.0-SNAPSHOT</version>\n\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-05-tomcat</module>\n        <module>lab-05-tomcat-apr</module>\n        <module>lab-05-jetty</module>\n        <module>lab-05-undertow</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-05-benchmark-tomcat-jetty-undertow/《性能测试 —— Tomcat、Jetty、Undertow 基准测试》.md",
    "content": "<http://www.iocoder.cn/Performance-Testing/Tomcat-Jetty-Undertow-benchmark/?github>\n"
  },
  {
    "path": "lab-06/lab-06-springmvc-tomcat/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-06</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-06-springmvc-tomcat</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <!-- 提供给 mapstruct 使用 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <configuration>\n                    <fork>true</fork>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-06/lab-06-springmvc-tomcat/src/main/java/cn/iocoder/springboot/labs/lab06/springmvc/Controller.java",
    "content": "package cn.iocoder.springboot.labs.lab06.springmvc;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class Controller {\n\n    @GetMapping(\"/hello\")\n    public String hello() {\n//        System.out.println(Thread.currentThread().getName());\n        return \"world\";\n    }\n\n    @GetMapping(\"/sleep\")\n    public String sleep() throws InterruptedException {\n        Thread.sleep(100L);\n//        System.out.println(Thread.currentThread().getName());\n        return \"world\";\n    }\n\n}\n"
  },
  {
    "path": "lab-06/lab-06-springmvc-tomcat/src/main/java/cn/iocoder/springboot/labs/lab06/springmvc/SpringMVCApplication.java",
    "content": "package cn.iocoder.springboot.labs.lab06.springmvc;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SpringMVCApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SpringMVCApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "lab-06/lab-06-webflux-netty/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-06</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-06-webflux-netty</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <!-- 提供给 mapstruct 使用 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <configuration>\n                    <fork>true</fork>\n                    <mainClass>cn.iocoder.springboot.labs.lab06.webflux.WebfluxNettyApplication</mainClass>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-06/lab-06-webflux-netty/src/main/java/cn/iocoder/springboot/labs/lab06/webflux/Controller.java",
    "content": "package cn.iocoder.springboot.labs.lab06.webflux;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport reactor.core.publisher.Mono;\nimport reactor.core.scheduler.Schedulers;\n\n@RestController\npublic class Controller {\n\n    @GetMapping(\"/hello\")\n    public Mono<String> hello() {\n        return Mono.just(\"world\");\n    }\n\n    @GetMapping(\"/sleep_direct\")\n    public Mono<String> sleepDirect() throws InterruptedException {\n        Thread.sleep(100L);\n        return Mono.just(\"world\");\n    }\n\n    @GetMapping(\"/sleep\")\n    public Mono<String> sleep() {\n//        System.out.println(Thread.currentThread().getName());\n//        return Mono.just(\"world\").delayElement(Duration.ofMillis(100));\n        return Mono.defer(() -> {\n//            System.out.println(Thread.currentThread().getName());\n            try {\n                Thread.sleep(100L);\n            } catch (InterruptedException ignored) {\n            }\n            return Mono.just(\"world\");\n        }).subscribeOn(Schedulers.parallel());\n    }\n\n}\n"
  },
  {
    "path": "lab-06/lab-06-webflux-netty/src/main/java/cn/iocoder/springboot/labs/lab06/webflux/WebfluxNettyApplication.java",
    "content": "package cn.iocoder.springboot.labs.lab06.webflux;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class WebfluxNettyApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(WebfluxNettyApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "lab-06/lab-06-webflux-tomcat/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-06</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-06-webflux-tomcat</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n            <exclusions>\n                <exclusion>\n                    <groupId>org.springframework.boot</groupId>\n                    <artifactId>spring-boot-starter-netty</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-tomcat</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <!-- 提供给 mapstruct 使用 -->\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <configuration>\n                    <fork>true</fork>\n                    <mainClass>cn.iocoder.springboot.labs.lab06.webflux.WebfluxTomcatApplication</mainClass>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-06/lab-06-webflux-tomcat/src/main/java/cn/iocoder/springboot/labs/lab06/webflux/Controller.java",
    "content": "package cn.iocoder.springboot.labs.lab06.webflux;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport reactor.core.publisher.Mono;\nimport reactor.core.scheduler.Schedulers;\n\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n@RestController\npublic class Controller {\n\n    @GetMapping(\"/hello\")\n    public Mono<String> hello() {\n        return Mono.just(\"world\");\n    }\n\n    @GetMapping(\"/sleep_direct\")\n    public Mono<String> sleepDirect() throws InterruptedException {\n        Thread.sleep(100L);\n        return Mono.just(\"world\");\n    }\n\n    @GetMapping(\"/sleep\")\n    public Mono<String> sleep() {\n//        System.out.println(Thread.currentThread().getName());\n//        return Mono.just(\"world\").delayElement(Duration.ofMillis(100));\n        return Mono.defer(() -> {\n//            System.out.println(Thread.currentThread().getName());\n            try {\n                Thread.sleep(100L);\n            } catch (InterruptedException ignored) {\n            }\n            return Mono.just(\"world\");\n        }).subscribeOn(Schedulers.parallel());\n    }\n\n    private Map<String, Boolean> MAP = new ConcurrentHashMap<>();\n\n    @GetMapping(\"/sleep2\")\n    public Mono<String> sleep2() {\n        return Mono.defer(() -> {\n            try {\n//                System.out.println(Thread.currentThread().getName());\n                if (!MAP.containsKey(Thread.currentThread().getName())) {\n                    System.out.println(Thread.currentThread().getName());\n                    MAP.put(Thread.currentThread().getName(), true);\n                }\n                Thread.sleep(100L);\n            } catch (InterruptedException ignored) {\n            }\n            return Mono.just(\"world\");\n        }).subscribeOn(Schedulers.elastic());\n    }\n\n}\n"
  },
  {
    "path": "lab-06/lab-06-webflux-tomcat/src/main/java/cn/iocoder/springboot/labs/lab06/webflux/WebfluxTomcatApplication.java",
    "content": "package cn.iocoder.springboot.labs.lab06.webflux;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class WebfluxTomcatApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(WebfluxTomcatApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "lab-06/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cn.iocoder.springboot.labs</groupId>\n    <artifactId>lab-06</artifactId>\n    <version>1.0-SNAPSHOT</version>\n\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-06-springmvc-tomcat</module>\n        <module>lab-06-webflux-netty</module>\n        <module>lab-06-webflux-tomcat</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-06/《性能测试 —— SpringMVC、Webflux 基准测试》.md",
    "content": "<http://www.iocoder.cn/Performance-Testing/SpringMVC-Webflux-benchmark/?github>\n"
  },
  {
    "path": "lab-07/lab-07-spring-cloud-gateway/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-07-spring-cloud-gateway</artifactId>\n    <version>1.0.0</version>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n        <spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-gateway</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>io.projectreactor</groupId>\n            <artifactId>reactor-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring-cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-07/lab-07-spring-cloud-gateway/src/main/java/cn/iocoder/springboot/labs/lab07/springcloudgateway/SpringCloudGatewayApplication.java",
    "content": "package cn.iocoder.springboot.labs.lab07.springcloudgateway;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.gateway.route.RouteLocator;\nimport org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;\nimport org.springframework.context.annotation.Bean;\n\n@SpringBootApplication\npublic class SpringCloudGatewayApplication {\n\n    @Bean\n    public RouteLocator myRouteLocator(RouteLocatorBuilder builder) {\n        return builder.routes()\n                .route(r -> r.path(\"/hello.txt\")\n                    .uri(\"http://localhost:8000/hello.txt\"))\n                .route(r -> r.path(\"/**\")\n                        .uri(\"http://localhost:8080\"))\n                .build();\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(SpringCloudGatewayApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-07/lab-07-spring-cloud-gateway/src/main/resources/application.yml",
    "content": "server:\n  port: 8081\n"
  },
  {
    "path": "lab-07/lab-07-zuul/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-07-zuul</artifactId>\n    <version>1.0.0</version>\n\n    <properties>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n        <java.version>1.8</java.version>\n        <spring-cloud.version>Greenwich.RELEASE</spring-cloud.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring-cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-07/lab-07-zuul/src/main/java/cn/iocoder/springboot/labs/lab07/zuul/ZuulApplication.java",
    "content": "package cn.iocoder.springboot.labs.lab07.zuul;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.zuul.EnableZuulProxy;\n\n@EnableZuulProxy\n@SpringBootApplication\npublic class ZuulApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ZuulApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-07/lab-07-zuul/src/main/resources/application.yml",
    "content": "zuul:\n  routes:\n    static:\n      path: /hello.txt\n      url: http://127.0.0.1:8000/\n      stripPrefix: false\n    api:\n      path: /**\n      url: http://127.0.0.1:8080\n  include-debug-header: true\n\nserver:\n  port: 8081\n"
  },
  {
    "path": "lab-07/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-07</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-07-spring-cloud-gateway</module>\n        <module>lab-07-zuul</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-07/《性能测试 —— Spring Cloud Gateway、Zuul 基准测试》.md",
    "content": "<http://www.iocoder.cn/Performance-Testing/SpringMVC-Webflux-benchmark/?github>\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-unit-test/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-07-spring-data-redis-unit-test</artifactId>\n\n    <dependencies>\n\n        <!-- 实现对 Redisson 的自动化配置 -->\n        <dependency>\n            <groupId>org.redisson</groupId>\n            <artifactId>redisson-spring-boot-starter</artifactId>\n            <version>3.11.3</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <!-- 等会示例会使用 fastjson 作为 JSON 序列化的工具 -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n            <version>1.2.61</version>\n        </dependency>\n\n        <!-- Spring Data Redis 默认使用 Jackson 作为 JSON 序列化的工具 -->\n        <dependency>\n            <groupId>com.fasterxml.jackson.core</groupId>\n            <artifactId>jackson-databind</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>commons-io</groupId>\n            <artifactId>commons-io</artifactId>\n            <version>2.6</version>\n        </dependency>\n\n        <!-- 内嵌 Redis 服务，用于单元测试 -->\n        <dependency>\n            <groupId>it.ozimov</groupId>\n            <artifactId>embedded-redis</artifactId>\n            <version>0.7.2</version>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-unit-test/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/Application.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n//@EnableTransactionManagement\npublic class Application {\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-unit-test/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/config/RedisConfiguration.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.serializer.RedisSerializer;\n\n@Configuration\npublic class RedisConfiguration {\n\n    @Bean\n    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {\n        // 创建 RedisTemplate 对象\n        RedisTemplate<String, Object> template = new RedisTemplate<>();\n\n        // 设置开启事务支持\n        template.setEnableTransactionSupport(true);\n\n        // 设置 RedisConnection 工厂。😈 它就是实现多种 Java Redis 客户端接入的秘密工厂。感兴趣的胖友，可以自己去撸下。\n        template.setConnectionFactory(factory);\n\n        // 使用 String 序列化方式，序列化 KEY 。\n        template.setKeySerializer(RedisSerializer.string());\n\n        // 使用 JSON 序列化方式（库是 Jackson ），序列化 VALUE 。\n        template.setValueSerializer(RedisSerializer.json());\n        return template;\n    }\n\n    //        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);\n//        ObjectMapper objectMapper = new ObjectMapper();// <1>\n////        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);\n////        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);\n//\n//        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);\n//        template.setValueSerializer(jackson2JsonRedisSerializer);\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-unit-test/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/util/JSONUtil.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.util;\n\nimport com.alibaba.fastjson.JSON;\n\n/**\n * JSON 工具类\n */\npublic class JSONUtil {\n\n    public static  <T> T parseObject(String text, Class<T> clazz) {\n        return JSON.parseObject(text, clazz);\n    }\n\n    public static String toJSONString(Object javaObject) {\n        return JSON.toJSONString(javaObject);\n    }\n\n    public static byte[] toJSONBytes(Object javaObject) {\n        return JSON.toJSONBytes(javaObject);\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-unit-test/src/main/resources/application.yml",
    "content": "spring:\n  # 对应 RedisProperties 类\n  redis:\n    host: 127.0.0.1\n    port: 6379\n#    password: # Redis 服务器密码，默认为空。生产中，一定要设置 Redis 密码！\n    database: 0 # Redis 数据库号，默认为 0 。\n    timeout: 0 # Redis 连接超时时间，单位：毫秒。\n    # 对应 RedissonProperties 类\n#    redisson:\n#      config: classpath:redisson.yml # 具体的每个配置项，见 org.redisson.config.Config 类。\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-unit-test/src/main/resources/redisson.yml",
    "content": ""
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-unit-test/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/Test01.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.config.TestRedisConfiguration;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.data.redis.connection.RedisConnection;\nimport org.springframework.data.redis.core.RedisCallback;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = TestRedisConfiguration.class)\npublic class Test01 {\n\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    @Autowired\n    private RedisTemplate redisTemplate;\n\n//    @Autowired\n//    private RedisServer server;\n\n    @Test\n    public void test01() {\n        // 写入\n        stringRedisTemplate.opsForValue().set(\"yunai\", \"shuai\");\n        // 读取\n        String value = stringRedisTemplate.opsForValue().get(\"yunai\");\n        Assert.assertEquals(\"值不匹配\", \"shuai\", value);\n\n        // 测试重启后读取\n        redisTemplate.execute(new RedisCallback() {\n            @Override\n            public Object doInRedis(RedisConnection connection) throws DataAccessException {\n                connection.flushDb();\n                return \"\";\n            }\n        });\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-unit-test/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/config/TestRedisConfiguration.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.config;\n\nimport org.springframework.boot.autoconfigure.data.redis.RedisProperties;\nimport org.springframework.boot.test.context.TestConfiguration;\nimport redis.embedded.RedisServer;\n\nimport javax.annotation.PostConstruct;\nimport javax.annotation.PreDestroy;\n\n@TestConfiguration\npublic class TestRedisConfiguration {\n\n    private RedisServer redisServer;\n\n    public TestRedisConfiguration(RedisProperties redisProperties) {\n        this.redisServer = new RedisServer(redisProperties.getPort());\n    }\n\n//    @Bean\n//    public RedisServer redisServer() {\n//        return redisServer;\n//    }\n\n    @PostConstruct\n    public void postConstruct() {\n        redisServer.start();\n    }\n\n    @PreDestroy\n    public void preDestroy() {\n        redisServer.stop();\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-07-spring-data-redis-with-jedis</artifactId>\n\n    <dependencies>\n\n        <!-- 实现对 Spring Data Redis 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis</artifactId>\n            <exclusions>\n                <!-- 去掉对 Lettuce 的依赖，因为 Spring Boot 优先使用 Lettuce 作为 Redis 客户端 -->\n                <exclusion>\n                    <groupId>io.lettuce</groupId>\n                    <artifactId>lettuce-core</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <!-- 引入 Jedis 的依赖，这样 Spring Boot 实现对 Jedis 的自动化配置 -->\n        <dependency>\n            <groupId>redis.clients</groupId>\n            <artifactId>jedis</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <!-- 等会示例会使用 fastjson 作为 JSON 序列化的工具 -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n            <version>1.2.61</version>\n        </dependency>\n\n        <!-- Spring Data Redis 默认使用 Jackson 作为 JSON 序列化的工具 -->\n        <dependency>\n            <groupId>com.fasterxml.jackson.core</groupId>\n            <artifactId>jackson-databind</artifactId>\n        </dependency>\n\n<!--        <dependency>-->\n<!--            <groupId>org.springframework</groupId>-->\n<!--            <artifactId>spring-tx</artifactId>-->\n<!--        </dependency>-->\n<!--        <dependency>-->\n<!--            <groupId>org.springframework</groupId>-->\n<!--            <artifactId>spring-jdbc</artifactId>-->\n<!--        </dependency>-->\n\n\n        <dependency>\n            <groupId>commons-io</groupId>\n            <artifactId>commons-io</artifactId>\n            <version>2.6</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/Application.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n//@EnableTransactionManagement\npublic class Application {\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/cacheobject/ProductCacheObject.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject;\n\n/**\n * 商品缓存对象\n */\npublic class ProductCacheObject {\n\n    /**\n     * 产品编号\n     */\n    private Integer id;\n    /**\n     * 产品名\n     */\n    private String name;\n    /**\n     * 产品分类编号\n     */\n    private Integer cid;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public ProductCacheObject setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public ProductCacheObject setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getCid() {\n        return cid;\n    }\n\n    public ProductCacheObject setCid(Integer cid) {\n        this.cid = cid;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ProductCacheObject{\" +\n                \"id=\" + id +\n                \", name='\" + name + '\\'' +\n                \", cid=\" + cid +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/cacheobject/UserCacheObject.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject;\n\n/**\n * 用户缓存对象\n */\npublic class UserCacheObject {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserCacheObject setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserCacheObject setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserCacheObject setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserCacheObject{\" +\n                \"id=\" + id +\n                \", name='\" + name + '\\'' +\n                \", gender=\" + gender +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/config/RedisConfiguration.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.config;\n\nimport cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.listener.TestChannelTopicMessageListener;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.listener.ChannelTopic;\nimport org.springframework.data.redis.listener.RedisMessageListenerContainer;\nimport org.springframework.data.redis.serializer.RedisSerializer;\n\n@Configuration\npublic class RedisConfiguration {\n\n    @Bean\n    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {\n        // 创建 RedisTemplate 对象\n        RedisTemplate<String, Object> template = new RedisTemplate<>();\n\n        // 设置开启事务支持\n        template.setEnableTransactionSupport(true);\n\n        // 设置 RedisConnection 工厂。😈 它就是实现多种 Java Redis 客户端接入的秘密工厂。感兴趣的胖友，可以自己去撸下。\n        template.setConnectionFactory(factory);\n\n        // 使用 String 序列化方式，序列化 KEY 。\n        template.setKeySerializer(RedisSerializer.string());\n\n        // 使用 JSON 序列化方式（库是 Jackson ），序列化 VALUE 。\n        template.setValueSerializer(RedisSerializer.json());\n        return template;\n    }\n\n    //        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);\n//        ObjectMapper objectMapper = new ObjectMapper();// <1>\n////        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);\n////        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);\n//\n//        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);\n//        template.setValueSerializer(jackson2JsonRedisSerializer);\n\n//    @Bean // PUB/SUB 使用的 Bean ，需要时打开。\n    public RedisMessageListenerContainer listenerContainer(RedisConnectionFactory factory) {\n        // 创建 RedisMessageListenerContainer 对象\n        RedisMessageListenerContainer container = new RedisMessageListenerContainer();\n\n        // 设置 RedisConnection 工厂。😈 它就是实现多种 Java Redis 客户端接入的秘密工厂。感兴趣的胖友，可以自己去撸下。\n        container.setConnectionFactory(factory);\n\n        // 添加监听器\n        container.addMessageListener(new TestChannelTopicMessageListener(), new ChannelTopic(\"TEST\"));\n//        container.addMessageListener(new TestChannelTopicMessageListener(), new ChannelTopic(\"AOTEMAN\"));\n//        container.addMessageListener(new TestPatternTopicMessageListener(), new PatternTopic(\"TEST\"));\n        return container;\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/dao/package-info.java",
    "content": "/**\n * 数据库访问层\n */\npackage cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.dao;\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/dao/redis/UserCacheDao.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.dao.redis;\n\nimport cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject.UserCacheObject;\nimport cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.util.JSONUtil;\nimport org.springframework.data.redis.core.ValueOperations;\nimport org.springframework.stereotype.Repository;\n\nimport javax.annotation.Resource;\n\n@Repository\npublic class UserCacheDao {\n\n    private static final String KEY_PATTERN = \"user:%d\"; // user:用户编号\n\n    @Resource(name = \"redisTemplate\")\n    @SuppressWarnings(\"SpringJavaInjectionPointsAutowiringInspection\")\n    private ValueOperations<String, String> operations;\n\n    private static String buildKey(Integer id) {\n        return String.format(KEY_PATTERN, id);\n    }\n\n    public UserCacheObject get(Integer id) {\n        String key = buildKey(id);\n        String value = operations.get(key);\n        return JSONUtil.parseObject(value, UserCacheObject.class);\n    }\n\n    public void set(Integer id, UserCacheObject object) {\n        String key = buildKey(id);\n        String value = JSONUtil.toJSONString(object);\n        operations.set(key, value);\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/listener/TestChannelTopicMessageListener.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.listener;\n\nimport org.springframework.data.redis.connection.Message;\nimport org.springframework.data.redis.connection.MessageListener;\n\npublic class TestChannelTopicMessageListener implements MessageListener {\n\n    @Override\n    public void onMessage(Message message, byte[] pattern) {\n        System.out.println(\"收到 ChannelTopic 消息：\");\n        System.out.println(\"线程编号：\" + Thread.currentThread().getName());\n        System.out.println(\"message：\" + message);\n        System.out.println(\"pattern：\" + new String(pattern));\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/listener/TestPatternTopicMessageListener.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.listener;\n\nimport org.springframework.data.redis.connection.Message;\nimport org.springframework.data.redis.connection.MessageListener;\n\npublic class TestPatternTopicMessageListener implements MessageListener {\n\n    @Override\n    public void onMessage(Message message, byte[] pattern) {\n        System.out.println(\"收到 PatternTopic 消息：\");\n        System.out.println(\"线程编号：\" + Thread.currentThread().getName());\n        System.out.println(\"message：\" + message);\n        System.out.println(\"pattern：\" + new String(pattern));\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/service/UserService01.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.service;\n\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class UserService01 {\n\n\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/service/UserService02.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.service;\n\nimport cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject.UserCacheObject;\nimport cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.dao.redis.UserCacheDao;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class UserService02 {\n\n    @Autowired\n    private UserCacheDao userCacheDao;\n\n    public UserCacheObject get(Integer id) {\n        return userCacheDao.get(id);\n    }\n\n    public void set(Integer id, UserCacheObject object) {\n        userCacheDao.set(id, object);\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/util/JSONUtil.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.util;\n\nimport com.alibaba.fastjson.JSON;\n\n/**\n * JSON 工具类\n */\npublic class JSONUtil {\n\n    public static  <T> T parseObject(String text, Class<T> clazz) {\n        return JSON.parseObject(text, clazz);\n    }\n\n    public static String toJSONString(Object javaObject) {\n        return JSON.toJSONString(javaObject);\n    }\n\n    public static byte[] toJSONBytes(Object javaObject) {\n        return JSON.toJSONBytes(javaObject);\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/resources/application.yml",
    "content": "spring:\n  # 对应 RedisProperties 类\n  redis:\n    host: 127.0.0.1\n    port: 6379\n    password: # Redis 服务器密码，默认为空。生产中，一定要设置 Redis 密码！\n    database: 0 # Redis 数据库号，默认为 0 。\n    timeout: 0 # Redis 连接超时时间，单位：毫秒。\n    # 对应 RedisProperties.Jedis 内部类\n    jedis:\n      pool:\n        max-active: 8 # 连接池最大连接数，默认为 8 。使用负数表示没有限制。\n        max-idle: 8 # 默认连接数最小空闲的连接数，默认为 8 。使用负数表示没有限制。\n        min-idle: 0 # 默认连接池最小空闲的连接数，默认为 0 。允许设置 0 和 正数。\n        max-wait: -1 # 连接池最大阻塞等待时间，单位：毫秒。默认为 -1 ，表示不限制。\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/resources/lua/compareAndSet.lua",
    "content": "if redis.call('GET', KEYS[1]) ~= ARGV[1] then\n    return 0\nend\nredis.call('SET', KEYS[1], ARGV[2])\nreturn 1\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/resources/lua/compareAndSet2.lua",
    "content": "if redis.call('GET', KEYS[1]) != ARGV[1] then\n    return {0}\nend\nredis.call('SET', KEYS[2], ARGV[2])\nreturn {1}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/main/resources/lua/test.lua",
    "content": "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/PipelineTest.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.data.redis.connection.RedisConnection;\nimport org.springframework.data.redis.core.RedisCallback;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.List;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class PipelineTest {\n\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    @Test\n    public void test01() {\n        List<Object> results = stringRedisTemplate.executePipelined(new RedisCallback<Object>() {\n\n            @Override\n            public Object doInRedis(RedisConnection connection) throws DataAccessException {\n                // set 写入\n                for (int i = 0; i < 3; i++) {\n                    connection.set(String.format(\"yunai:%d\", i).getBytes(), \"shuai\".getBytes());\n                }\n\n                // get\n                for (int i = 0; i < 3; i++) {\n                    connection.get(String.format(\"yunai:%d\", i).getBytes());\n                }\n\n                // 返回 null 即可\n                return null;\n            }\n        });\n\n        // 打印结果\n        System.out.println(results);\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/PubSubTest.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class PubSubTest {\n\n    public static final String TOPIC = \"TEST\";\n\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    @Test\n    public void test01() throws InterruptedException {\n        for (int i = 0; i < 3; i++) {\n            stringRedisTemplate.convertAndSend(TOPIC, \"yunai:\" + i);\n            Thread.sleep(1000L);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/ScriptTest.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport org.apache.commons.io.IOUtils;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.data.redis.core.script.DefaultRedisScript;\nimport org.springframework.data.redis.core.script.RedisScript;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class ScriptTest {\n\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    @Test\n    public void test01() throws IOException {\n        // 读取 /resources/lua/compareAndSet.lua 脚本 。注意，需要引入下 commons-io 依赖。\n        String  scriptContents = IOUtils.toString(getClass().getResourceAsStream(\"/lua/compareAndSet.lua\"), \"UTF-8\");\n        // 创建 RedisScript 对象\n        RedisScript<Long> script = new DefaultRedisScript<>(scriptContents, Long.class);\n        // 执行 LUA 脚本\n        Long result = stringRedisTemplate.execute(script, Collections.singletonList(\"yunai:1\"), \"shuai02\", \"shuai\");\n        System.out.println(result);\n    }\n\n    @Test\n    public void test02() throws IOException {\n        // 读取 /resources/lua/compareAndSet.lua 脚本 。注意，需要引入下 commons-io 依赖。\n        String  scriptContents = IOUtils.toString(getClass().getResourceAsStream(\"/lua/test.lua\"), \"UTF-8\");\n        // 创建 RedisScript 对象\n        RedisScript<List> script = new DefaultRedisScript<>(scriptContents, List.class);\n\n        List<Object> result = stringRedisTemplate.execute(script, Arrays.asList(\"key1\", \"key2\"), \"first\", \"second\");\n        System.out.println(result);\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/SessionTest.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.data.redis.core.RedisOperations;\nimport org.springframework.data.redis.core.SessionCallback;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class SessionTest {\n\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    @Test\n    public void test01() {\n        String result = stringRedisTemplate.execute(new SessionCallback<String>() {\n\n            @Override\n            public String execute(RedisOperations operations) throws DataAccessException {\n                for (int i = 0; i < 100; i++) {\n                    operations.opsForValue().set(String.format(\"yunai:%d\", i), \"shuai02\");\n                }\n                return (String) operations.opsForValue().get(String.format(\"yunai:%d\", 0));\n            }\n\n        });\n\n        System.out.println(\"result:\" + result);\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/Test01.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject.UserCacheObject;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class Test01 {\n\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    @Autowired\n    private RedisTemplate redisTemplate;\n\n    @Test\n    public void testStringSetKey() {\n        stringRedisTemplate.opsForValue().set(\"yunai\", \"shuai\");\n    }\n\n    @Test\n    public void testStringSetKey02() {\n        redisTemplate.opsForValue().set(\"yunai\", \"shuai\");\n    }\n\n    @Test\n    public void testSetAdd() {\n        stringRedisTemplate.opsForSet().add(\"yunai_descriptions\", \"shuai\", \"cai\");\n    }\n\n    @Test\n    public void testStringSetKeyUserCache() {\n        UserCacheObject object = new UserCacheObject()\n                .setId(1)\n                .setName(\"芋道源码\")\n                .setGender(1); // 男\n        String key = String.format(\"user:%d\", object.getId());\n        redisTemplate.opsForValue().set(key, object);\n    }\n\n    @Test\n    public void testStringGetKeyUserCache() {\n        String key = String.format(\"user:%d\", 1);\n        Object value = redisTemplate.opsForValue().get(key);\n        System.out.println(value);\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/TransactionTest.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\n//@EnableTransactionManagement\npublic class TransactionTest {\n\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    @Test\n    @Transactional\n    public void test01() {\n        // 这里是偷懒，没在 RedisConfiguration 配置类中，设置 stringRedisTemplate 开启事务。\n        stringRedisTemplate.setEnableTransactionSupport(true);\n\n        // 执行想要的操作\n        stringRedisTemplate.opsForValue().set(\"yunai:1\", \"shuai\");\n        stringRedisTemplate.opsForValue().set(\"yudaoyuanma:1\", \"dai\");\n\n//        stringRedisTemplate.execute(new SessionCallback<String>() {\n//\n//            @Override\n//            public <K, V> String execute(RedisOperations<K, V> operations) throws DataAccessException {\n//                return null;\n//            }\n//\n////            @Override\n////            public List<String> execute(RedisOperations<String, String> operations) throws DataAccessException {\n////                for (int i = 0; i < 100; i++) {\n////                    operations.opsForValue(String.format(\"yunai:%d\", i), \"shuai\");\n////                }\n////                return null;\n////            }\n//\n//        });\n    }\n\n    @Test\n    public void test02() {\n        stringRedisTemplate.setEnableTransactionSupport(true);\n\n\n        stringRedisTemplate.opsForValue().get(\"user:1\");\n        stringRedisTemplate.opsForValue().get(\"user:2\");\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/UserService02Test.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject.UserCacheObject;\nimport cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.service.UserService02;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserService02Test {\n\n    @Autowired\n    private UserService02 userService;\n\n    @Test\n    public void testSet() {\n        UserCacheObject object = new UserCacheObject()\n                .setId(1)\n                .setName(\"芋道源码\")\n                .setGender(1); // 男\n        userService.set(object.getId(), object);\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-jedis/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/package-info.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-07-spring-data-redis-with-redisson</artifactId>\n\n    <dependencies>\n\n        <!-- 实现对 Redisson 的自动化配置 -->\n        <dependency>\n            <groupId>org.redisson</groupId>\n            <artifactId>redisson-spring-boot-starter</artifactId>\n            <version>3.11.3</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <!-- 等会示例会使用 fastjson 作为 JSON 序列化的工具 -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n            <version>1.2.61</version>\n        </dependency>\n\n        <!-- Spring Data Redis 默认使用 Jackson 作为 JSON 序列化的工具 -->\n        <dependency>\n            <groupId>com.fasterxml.jackson.core</groupId>\n            <artifactId>jackson-databind</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>commons-io</groupId>\n            <artifactId>commons-io</artifactId>\n            <version>2.6</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/Application.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n//@EnableTransactionManagement\npublic class Application {\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/cacheobject/ProductCacheObject.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject;\n\n/**\n * 商品缓存对象\n */\npublic class ProductCacheObject {\n\n    /**\n     * 产品编号\n     */\n    private Integer id;\n    /**\n     * 产品名\n     */\n    private String name;\n    /**\n     * 产品分类编号\n     */\n    private Integer cid;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public ProductCacheObject setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public ProductCacheObject setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getCid() {\n        return cid;\n    }\n\n    public ProductCacheObject setCid(Integer cid) {\n        this.cid = cid;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ProductCacheObject{\" +\n                \"id=\" + id +\n                \", name='\" + name + '\\'' +\n                \", cid=\" + cid +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/cacheobject/UserCacheObject.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject;\n\n/**\n * 用户缓存对象\n */\npublic class UserCacheObject {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserCacheObject setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserCacheObject setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserCacheObject setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserCacheObject{\" +\n                \"id=\" + id +\n                \", name='\" + name + '\\'' +\n                \", gender=\" + gender +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/config/RedisConfiguration.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.config;\n\nimport cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.listener.TestChannelTopicMessageListener;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.listener.ChannelTopic;\nimport org.springframework.data.redis.listener.RedisMessageListenerContainer;\nimport org.springframework.data.redis.serializer.RedisSerializer;\n\n@Configuration\npublic class RedisConfiguration {\n\n    @Bean\n    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {\n        // 创建 RedisTemplate 对象\n        RedisTemplate<String, Object> template = new RedisTemplate<>();\n\n        // 设置开启事务支持\n        template.setEnableTransactionSupport(true);\n\n        // 设置 RedisConnection 工厂。😈 它就是实现多种 Java Redis 客户端接入的秘密工厂。感兴趣的胖友，可以自己去撸下。\n        template.setConnectionFactory(factory);\n\n        // 使用 String 序列化方式，序列化 KEY 。\n        template.setKeySerializer(RedisSerializer.string());\n\n        // 使用 JSON 序列化方式（库是 Jackson ），序列化 VALUE 。\n        template.setValueSerializer(RedisSerializer.json());\n        return template;\n    }\n\n    //        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);\n//        ObjectMapper objectMapper = new ObjectMapper();// <1>\n////        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);\n////        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);\n//\n//        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);\n//        template.setValueSerializer(jackson2JsonRedisSerializer);\n\n//    @Bean // PUB/SUB 使用的 Bean ，需要时打开。\n    public RedisMessageListenerContainer listenerContainer(RedisConnectionFactory factory) {\n        // 创建 RedisMessageListenerContainer 对象\n        RedisMessageListenerContainer container = new RedisMessageListenerContainer();\n\n        // 设置 RedisConnection 工厂。😈 它就是实现多种 Java Redis 客户端接入的秘密工厂。感兴趣的胖友，可以自己去撸下。\n        container.setConnectionFactory(factory);\n\n        // 添加监听器\n        container.addMessageListener(new TestChannelTopicMessageListener(), new ChannelTopic(\"TEST\"));\n//        container.addMessageListener(new TestChannelTopicMessageListener(), new ChannelTopic(\"AOTEMAN\"));\n//        container.addMessageListener(new TestPatternTopicMessageListener(), new PatternTopic(\"TEST\"));\n        return container;\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/dao/package-info.java",
    "content": "/**\n * 数据库访问层\n */\npackage cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.dao;\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/dao/redis/UserCacheDao.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.dao.redis;\n\nimport cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject.UserCacheObject;\nimport cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.util.JSONUtil;\nimport org.springframework.data.redis.core.ValueOperations;\nimport org.springframework.stereotype.Repository;\n\nimport javax.annotation.Resource;\n\n@Repository\npublic class UserCacheDao {\n\n    private static final String KEY_PATTERN = \"user:%d\"; // user:用户编号\n\n    @Resource(name = \"redisTemplate\")\n    @SuppressWarnings(\"SpringJavaInjectionPointsAutowiringInspection\")\n    private ValueOperations<String, String> operations;\n\n    private static String buildKey(Integer id) {\n        return String.format(KEY_PATTERN, id);\n    }\n\n    public UserCacheObject get(Integer id) {\n        String key = buildKey(id);\n        String value = operations.get(key);\n        return JSONUtil.parseObject(value, UserCacheObject.class);\n    }\n\n    public void set(Integer id, UserCacheObject object) {\n        String key = buildKey(id);\n        String value = JSONUtil.toJSONString(object);\n        operations.set(key, value);\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/listener/TestChannelTopicMessageListener.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.listener;\n\nimport org.springframework.data.redis.connection.Message;\nimport org.springframework.data.redis.connection.MessageListener;\n\npublic class TestChannelTopicMessageListener implements MessageListener {\n\n    @Override\n    public void onMessage(Message message, byte[] pattern) {\n        System.out.println(\"收到 ChannelTopic 消息：\");\n        System.out.println(\"线程编号：\" + Thread.currentThread().getName());\n        System.out.println(\"message：\" + message);\n        System.out.println(\"pattern：\" + new String(pattern));\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/listener/TestPatternTopicMessageListener.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.listener;\n\nimport org.springframework.data.redis.connection.Message;\nimport org.springframework.data.redis.connection.MessageListener;\n\npublic class TestPatternTopicMessageListener implements MessageListener {\n\n    @Override\n    public void onMessage(Message message, byte[] pattern) {\n        System.out.println(\"收到 PatternTopic 消息：\");\n        System.out.println(\"线程编号：\" + Thread.currentThread().getName());\n        System.out.println(\"message：\" + message);\n        System.out.println(\"pattern：\" + new String(pattern));\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/service/UserService01.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.service;\n\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class UserService01 {\n\n\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/service/UserService02.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.service;\n\nimport cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject.UserCacheObject;\nimport cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.dao.redis.UserCacheDao;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class UserService02 {\n\n    @Autowired\n    private UserCacheDao userCacheDao;\n\n    public UserCacheObject get(Integer id) {\n        return userCacheDao.get(id);\n    }\n\n    public void set(Integer id, UserCacheObject object) {\n        userCacheDao.set(id, object);\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/util/JSONUtil.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.util;\n\nimport com.alibaba.fastjson.JSON;\n\n/**\n * JSON 工具类\n */\npublic class JSONUtil {\n\n    public static  <T> T parseObject(String text, Class<T> clazz) {\n        return JSON.parseObject(text, clazz);\n    }\n\n    public static String toJSONString(Object javaObject) {\n        return JSON.toJSONString(javaObject);\n    }\n\n    public static byte[] toJSONBytes(Object javaObject) {\n        return JSON.toJSONBytes(javaObject);\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/resources/application.yml",
    "content": "spring:\n  # 对应 RedisProperties 类\n  redis:\n    host: 127.0.0.1\n    port: 6379\n#    password: # Redis 服务器密码，默认为空。生产中，一定要设置 Redis 密码！\n    database: 0 # Redis 数据库号，默认为 0 。\n    timeout: 0 # Redis 连接超时时间，单位：毫秒。\n    # 对应 RedissonProperties 类\n#    redisson:\n#      config: classpath:redisson.yml # 具体的每个配置项，见 org.redisson.config.Config 类。\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/resources/lua/compareAndSet.lua",
    "content": "if redis.call('GET', KEYS[1]) ~= ARGV[1] then\n    return 0\nend\nredis.call('SET', KEYS[1], ARGV[2])\nreturn 1\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/resources/lua/compareAndSet2.lua",
    "content": "if redis.call('GET', KEYS[1]) != ARGV[1] then\n    return {0}\nend\nredis.call('SET', KEYS[2], ARGV[2])\nreturn {1}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/resources/lua/test.lua",
    "content": "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/main/resources/redisson.yml",
    "content": ""
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/LockTest.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.redisson.api.RLock;\nimport org.redisson.api.RedissonClient;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.concurrent.TimeUnit;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class LockTest {\n\n    private static final String LOCK_KEY = \"anylock\";\n\n    @Autowired\n    private RedissonClient redissonClient;\n\n    @Test\n    public void test() throws InterruptedException {\n        // 启动一个线程 A ，去占有锁\n        new Thread(new Runnable() {\n            @Override\n            public void run() {\n                // 加锁以后 10 秒钟自动解锁\n                // 无需调用 unlock 方法手动解锁\n                final RLock lock = redissonClient.getLock(LOCK_KEY);\n                lock.lock(10, TimeUnit.SECONDS);\n            }\n        }).start();\n        // 简单 sleep 1 秒，保证线程 A 成功持有锁\n        Thread.sleep(1000L);\n\n        // 尝试加锁，最多等待 100 秒，上锁以后 10 秒自动解锁\n        System.out.println(String.format(\"准备开始获得锁时间：%s\", new SimpleDateFormat(\"yyyy-MM-DD HH:mm:ss\").format(new Date())));\n        final RLock lock = redissonClient.getLock(LOCK_KEY);\n        boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);\n        if (res) {\n            System.out.println(String.format(\"实际获得锁时间：%s\", new SimpleDateFormat(\"yyyy-MM-DD HH:mm:ss\").format(new Date())));\n        } else {\n            System.out.println(\"加锁失败\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/PipelineTest.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.data.redis.connection.RedisConnection;\nimport org.springframework.data.redis.core.RedisCallback;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.List;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class PipelineTest {\n\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    @Test\n    public void test01() {\n        List<Object> results = stringRedisTemplate.executePipelined(new RedisCallback<Object>() {\n\n            @Override\n            public Object doInRedis(RedisConnection connection) throws DataAccessException {\n                // set 写入\n                for (int i = 0; i < 3; i++) {\n                    connection.set(String.format(\"yunai:%d\", i).getBytes(), \"shuai\".getBytes());\n                }\n\n                // get\n                for (int i = 0; i < 3; i++) {\n                    connection.get(String.format(\"yunai:%d\", i).getBytes());\n                }\n\n                // 返回 null 即可\n                return null;\n            }\n        });\n\n        // 打印结果\n        System.out.println(results);\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/PubSubTest.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class PubSubTest {\n\n    public static final String TOPIC = \"TEST\";\n\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    @Test\n    public void test01() throws InterruptedException {\n        for (int i = 0; i < 3; i++) {\n            stringRedisTemplate.convertAndSend(TOPIC, \"yunai:\" + i);\n            Thread.sleep(1000L);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/RateLimiterTest.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.redisson.api.RRateLimiter;\nimport org.redisson.api.RateIntervalUnit;\nimport org.redisson.api.RateType;\nimport org.redisson.api.RedissonClient;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class RateLimiterTest {\n\n    @Autowired\n    private RedissonClient redissonClient;\n\n    @Test\n    public void test() throws InterruptedException {\n        // 创建 RRateLimiter 对象\n        RRateLimiter rateLimiter = redissonClient.getRateLimiter(\"myRateLimiter\");\n        // 初始化：最大流速 = 每 1 秒产生 2 个令牌\n        rateLimiter.trySetRate(RateType.OVERALL, 2, 1, RateIntervalUnit.SECONDS);\n//        rateLimiter.trySetRate(RateType.PER_CLIENT, 50, 1, RateIntervalUnit.MINUTES);\n\n        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n        for (int i = 0; i < 5; i++) {\n            System.out.println(String.format(\"%s：获得锁结果(%s)\", simpleDateFormat.format(new Date()),\n                    rateLimiter.tryAcquire()));\n            Thread.sleep(250L);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/ScriptTest.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport org.apache.commons.io.IOUtils;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.data.redis.core.script.DefaultRedisScript;\nimport org.springframework.data.redis.core.script.RedisScript;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class ScriptTest {\n\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    @Test\n    public void test01() throws IOException {\n        // 读取 /resources/lua/compareAndSet.lua 脚本 。注意，需要引入下 commons-io 依赖。\n        String  scriptContents = IOUtils.toString(getClass().getResourceAsStream(\"/lua/compareAndSet.lua\"), \"UTF-8\");\n        // 创建 RedisScript 对象\n        RedisScript<Long> script = new DefaultRedisScript<>(scriptContents, Long.class);\n        // 执行 LUA 脚本\n        Long result = stringRedisTemplate.execute(script, Collections.singletonList(\"yunai:1\"), \"shuai02\", \"shuai\");\n        System.out.println(result);\n    }\n\n    @Test\n    public void test02() throws IOException {\n        // 读取 /resources/lua/compareAndSet.lua 脚本 。注意，需要引入下 commons-io 依赖。\n        String  scriptContents = IOUtils.toString(getClass().getResourceAsStream(\"/lua/test.lua\"), \"UTF-8\");\n        // 创建 RedisScript 对象\n        RedisScript<List> script = new DefaultRedisScript<>(scriptContents, List.class);\n\n        List<Object> result = stringRedisTemplate.execute(script, Arrays.asList(\"key1\", \"key2\"), \"first\", \"second\");\n        System.out.println(result);\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/SessionTest.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.dao.DataAccessException;\nimport org.springframework.data.redis.core.RedisOperations;\nimport org.springframework.data.redis.core.SessionCallback;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class SessionTest {\n\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    @Test\n    public void test01() {\n        String result = stringRedisTemplate.execute(new SessionCallback<String>() {\n\n            @Override\n            public String execute(RedisOperations operations) throws DataAccessException {\n                for (int i = 0; i < 100; i++) {\n                    operations.opsForValue().set(String.format(\"yunai:%d\", i), \"shuai02\");\n                }\n                return (String) operations.opsForValue().get(String.format(\"yunai:%d\", 0));\n            }\n\n        });\n\n        System.out.println(\"result:\" + result);\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/Test01.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject.UserCacheObject;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.data.redis.core.RedisTemplate;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class Test01 {\n\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    @Autowired\n    private RedisTemplate redisTemplate;\n\n    @Test\n    public void testStringSetKey() {\n        stringRedisTemplate.opsForValue().set(\"yunai\", \"shuai\");\n    }\n\n    @Test\n    public void testStringSetKey02() {\n        redisTemplate.opsForValue().set(\"yunai\", \"shuai\");\n    }\n\n    @Test\n    public void testSetAdd() {\n        stringRedisTemplate.opsForSet().add(\"yunai_descriptions\", \"shuai\", \"cai\");\n    }\n\n    @Test\n    public void testStringSetKeyUserCache() {\n        UserCacheObject object = new UserCacheObject()\n                .setId(1)\n                .setName(\"芋道源码\")\n                .setGender(1); // 男\n        String key = String.format(\"user:%d\", object.getId());\n        redisTemplate.opsForValue().set(key, object);\n    }\n\n    @Test\n    public void testStringGetKeyUserCache() {\n        String key = String.format(\"user:%d\", 1);\n        Object value = redisTemplate.opsForValue().get(key);\n        System.out.println(value);\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/TransactionTest.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.transaction.annotation.Transactional;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\n//@EnableTransactionManagement\npublic class TransactionTest {\n\n    @Autowired\n    private StringRedisTemplate stringRedisTemplate;\n\n    @Test\n    @Transactional\n    public void test01() {\n        // 这里是偷懒，没在 RedisConfiguration 配置类中，设置 stringRedisTemplate 开启事务。\n        stringRedisTemplate.setEnableTransactionSupport(true);\n\n        // 执行想要的操作\n        stringRedisTemplate.opsForValue().set(\"yunai:1\", \"shuai\");\n        stringRedisTemplate.opsForValue().set(\"yudaoyuanma:1\", \"dai\");\n\n//        stringRedisTemplate.execute(new SessionCallback<String>() {\n//\n//            @Override\n//            public <K, V> String execute(RedisOperations<K, V> operations) throws DataAccessException {\n//                return null;\n//            }\n//\n////            @Override\n////            public List<String> execute(RedisOperations<String, String> operations) throws DataAccessException {\n////                for (int i = 0; i < 100; i++) {\n////                    operations.opsForValue(String.format(\"yunai:%d\", i), \"shuai\");\n////                }\n////                return null;\n////            }\n//\n//        });\n    }\n\n    @Test\n    public void test02() {\n        stringRedisTemplate.setEnableTransactionSupport(true);\n\n\n        stringRedisTemplate.opsForValue().get(\"user:1\");\n        stringRedisTemplate.opsForValue().get(\"user:2\");\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/UserService02Test.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n\nimport cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.cacheobject.UserCacheObject;\nimport cn.iocoder.springboot.labs.lab10.springdatarediswithjedis.service.UserService02;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserService02Test {\n\n    @Autowired\n    private UserService02 userService;\n\n    @Test\n    public void testSet() {\n        UserCacheObject object = new UserCacheObject()\n                .setId(1)\n                .setName(\"芋道源码\")\n                .setGender(1); // 男\n        userService.set(object.getId(), object);\n    }\n\n}\n"
  },
  {
    "path": "lab-11-spring-data-redis/lab-07-spring-data-redis-with-redisson/src/test/java/cn/iocoder/springboot/labs/lab10/springdatarediswithjedis/package-info.java",
    "content": "package cn.iocoder.springboot.labs.lab10.springdatarediswithjedis;\n"
  },
  {
    "path": "lab-11-spring-data-redis/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-11-spring-data-redis</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-07-spring-data-redis-with-jedis</module>\n        <module>lab-07-spring-data-redis-with-redisson</module>\n        <module>lab-07-spring-data-redis-unit-test</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-11-spring-data-redis/《芋道 Spring Boot Redis 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Redis/?github>\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-annotation/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-12-mybatis-annotation</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.1</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-annotation/src/main/java/cn/iocoder/springboot/lab12/mybatis/Application.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis;\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@MapperScan(basePackages = \"cn.iocoder.springboot.lab12.mybatis.mapper\")\npublic class Application {\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-annotation/src/main/java/cn/iocoder/springboot/lab12/mybatis/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.dataobject;\n\nimport java.util.Date;\n\n/**\n * 用户 DO\n */\npublic class UserDO {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码（明文）\n     *\n     * ps：生产环境下，千万不要明文噢\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    public UserDO setCreateTime(Date createTime) {\n        this.createTime = createTime;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-annotation/src/main/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapper.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.mapper;\n\nimport cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO;\nimport org.apache.ibatis.annotations.*;\nimport org.springframework.stereotype.Repository;\n\nimport java.util.Collection;\nimport java.util.List;\n\n@Repository\npublic interface UserMapper {\n\n    @Insert(\"INSERT INTO users(username, password, create_time) VALUES(#{username}, #{password}, #{createTime})\")\n    @Options(useGeneratedKeys = true, keyProperty = \"id\", keyColumn = \"id\")\n    int insert(UserDO user);\n\n    @Update(value = {\n            \"<script>\",\n            \"UPDATE users\",\n            \"<set>\",\n            \"<if test='username != null'>, username = #{username}</if>\",\n            \"<if test='password != null'>, password = #{password}</if>\",\n            \"</set>\",\n            \"</script>\"\n    })\n    int updateById(UserDO user);\n\n    @Insert(\"DELETE FROM users WHERE id = #{id}\")\n    int deleteById(@Param(\"id\") Integer id); // 生产请使用标记删除，除非有点想不开，嘿嘿。\n\n    @Select(\"SELECT username, password, create_time FROM users WHERE id = #{id}\")\n    UserDO selectById(@Param(\"id\") Integer id);\n\n    @Select(\"SELECT username, password, create_time FROM users WHERE username = #{username}\")\n    UserDO selectByUsername(@Param(\"username\") String username);\n\n    @Select(value = {\n            \"<script>\",\n            \"SELECT username, password, create_time FROM users\",\n            \"WHERE id IN\",\n            \"<foreach item='id' collection='ids' separator=',' open='(' close=')' index=''>\",\n            \"#{id}\",\n            \"</foreach>\",\n            \"</script>\"\n    })\n    List<UserDO> selectByIds(@Param(\"ids\") Collection<Integer> ids);\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-annotation/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    url: jdbc:mysql://47.112.193.81:3306/testb5f4?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: testb5f4\n    password: F4df4db0ed86@11\n\n# mybatis 配置内容\nmybatis:\n  config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径\n#  mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址\n  type-aliases-package: cn.iocoder.springboot.lab12.mybatis.dataobject # 配置数据库实体包路径\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-annotation/src/main/resources/mapper/UserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.springboot.lab12.mybatis.mapper.UserMapper\">\n\n    <sql id=\"FIELDS\">\n        id, username, password, create_time\n    </sql>\n\n    <insert id=\"insert\" parameterType=\"UserDO\" useGeneratedKeys=\"true\" keyProperty=\"id\">\n        INSERT INTO users (\n          username, password, create_time\n        ) VALUES (\n          #{username}, #{password}, #{createTime}\n        )\n    </insert>\n\n    <update id=\"updateById\" parameterType=\"UserDO\">\n        UPDATE users\n        <set>\n            <if test=\"username != null\">\n                , username = #{username}\n            </if>\n            <if test=\"password != null\">\n                , password = #{password}\n            </if>\n        </set>\n        WHERE id = #{id}\n    </update>\n\n    <delete id=\"deleteById\" parameterType=\"Integer\">\n        DELETE FROM users\n        WHERE id = #{id}\n    </delete>\n\n    <select id=\"selectById\" parameterType=\"Integer\" resultType=\"UserDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM users\n        WHERE id = #{id}\n    </select>\n\n    <select id=\"selectByUsername\" parameterType=\"String\" resultType=\"UserDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM users\n        WHERE username = #{username}\n        LIMIT 1\n    </select>\n\n    <select id=\"selectByIds\" resultType=\"UserDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM users\n        WHERE id IN\n            <foreach item=\"id\" collection=\"ids\" separator=\",\" open=\"(\" close=\")\" index=\"\">\n                #{id}\n            </foreach>\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-annotation/src/main/resources/mybatis-config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE configuration PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n<configuration>\n\n    <settings>\n        <!-- 使用驼峰命名法转换字段。 -->\n        <setting name=\"mapUnderscoreToCamelCase\" value=\"true\"/>\n    </settings>\n\n    <typeAliases>\n        <typeAlias alias=\"Integer\" type=\"java.lang.Integer\"/>\n        <typeAlias alias=\"Long\" type=\"java.lang.Long\"/>\n        <typeAlias alias=\"HashMap\" type=\"java.util.HashMap\"/>\n        <typeAlias alias=\"LinkedHashMap\" type=\"java.util.LinkedHashMap\"/>\n        <typeAlias alias=\"ArrayList\" type=\"java.util.ArrayList\"/>\n        <typeAlias alias=\"LinkedList\" type=\"java.util.LinkedList\"/>\n    </typeAliases>\n\n</configuration>\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-annotation/src/main/resources/sql/users.sql",
    "content": "CREATE TABLE `users` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',\n  `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',\n  `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码',\n  `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_username` (`username`)\n) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-annotation/src/test/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapperTest.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.mapper;\n\nimport cn.iocoder.springboot.lab12.mybatis.Application;\nimport cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.UUID;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserMapperTest {\n\n    @Autowired\n    private UserMapper userMapper;\n\n    @Test\n    public void testInsert() {\n        UserDO user = new UserDO().setUsername(UUID.randomUUID().toString())\n                .setPassword(\"nicai\").setCreateTime(new Date());\n        userMapper.insert(user);\n        System.out.println(user.getId());\n    }\n\n    @Test\n    public void testUpdateById() {\n        UserDO updateUser = new UserDO().setId(1)\n                .setPassword(\"wobucai\");\n        userMapper.updateById(updateUser);\n    }\n\n    @Test\n    public void testDeleteById() {\n        userMapper.deleteById(2);\n    }\n\n    @Test\n    public void testSelectById() {\n        userMapper.selectById(1);\n    }\n\n    @Test\n    public void testSelectByUsername() {\n        userMapper.selectByUsername(\"yunai\");\n    }\n\n    @Test\n    public void testSelectByIds() {\n        List<UserDO> users = userMapper.selectByIds(Arrays.asList(1, 3));\n        System.out.println(\"users：\" + users.size());\n    }\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-12-mybatis-plus</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis Plus 的自动化配置 -->\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus-boot-starter</artifactId>\n            <version>3.2.0</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus/src/main/java/cn/iocoder/springboot/lab12/mybatis/Application.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis;\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@MapperScan(basePackages = \"cn.iocoder.springboot.lab12.mybatis.mapper\")\npublic class Application {\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus/src/main/java/cn/iocoder/springboot/lab12/mybatis/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.dataobject;\n\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport com.baomidou.mybatisplus.annotation.TableName;\n\nimport java.util.Date;\n\n/**\n * 用户 DO\n */\n@TableName(value = \"users\")\npublic class UserDO {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码（明文）\n     *\n     * ps：生产环境下，千万不要明文噢\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n    /**\n     * 是否删除\n     */\n    @TableLogic\n    private Integer deleted;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    public UserDO setCreateTime(Date createTime) {\n        this.createTime = createTime;\n        return this;\n    }\n\n    public Integer getDeleted() {\n        return deleted;\n    }\n\n    public UserDO setDeleted(Integer deleted) {\n        this.deleted = deleted;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus/src/main/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapper.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.mapper;\n\nimport cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\n\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.List;\n\n@Repository\npublic interface UserMapper extends BaseMapper<UserDO> {\n\n    default UserDO selectByUsername(@Param(\"username\") String username) {\n        return selectOne(new QueryWrapper<UserDO>().eq(\"username\", username));\n    }\n\n    List<UserDO> selectByIds(@Param(\"ids\") Collection<Integer> ids);\n\n    default IPage<UserDO> selectPageByCreateTime(IPage<UserDO> page, @Param(\"createTime\") Date createTime) {\n        return selectPage(page,\n                new QueryWrapper<UserDO>().gt(\"create_time\", createTime)\n//                new QueryWrapper<UserDO>().like(\"username\", \"46683d9d\")\n        );\n    }\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    url: jdbc:mysql://47.112.193.81:3306/testb5f4?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: testb5f4\n    password: F4df4db0ed86@11\n\n# mybatis-plus 配置内容\nmybatis-plus:\n  configuration:\n    map-underscore-to-camel-case: true # 虽然默认为 true ，但是还是显示去指定下。\n  global-config:\n    db-config:\n      id-type: auto # ID 主键自增\n      logic-delete-value: 1 # 逻辑已删除值(默认为 1)\n      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)\n  mapper-locations: classpath*:mapper/*.xml\n  type-aliases-package: cn.iocoder.springboot.lab12.mybatis.dataobject\n\n# logging\nlogging:\n  level:\n    # dao 开启 debug 模式 mybatis 输入 sql\n    cn:\n      iocoder:\n        springboot:\n          lab12:\n            mybatis:\n              mapper: debug\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus/src/main/resources/mapper/UserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.springboot.lab12.mybatis.mapper.UserMapper\">\n\n    <sql id=\"FIELDS\">\n        id, username, password, create_time\n    </sql>\n\n    <select id=\"selectByIds\" resultType=\"UserDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM users\n        WHERE id IN\n            <foreach item=\"id\" collection=\"ids\" separator=\",\" open=\"(\" close=\")\" index=\"\">\n                #{id}\n            </foreach>\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus/src/main/resources/sql/users.sql",
    "content": "CREATE TABLE `users` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',\n  `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',\n  `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码',\n  `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n  `deleted` bit(1) DEFAULT NULL COMMENT '是否删除。0-未删除；1-删除',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_username` (`username`)\n) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus/src/test/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapperTest.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.mapper;\n\nimport cn.iocoder.springboot.lab12.mybatis.Application;\nimport cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.*;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserMapperTest {\n\n    @Autowired\n    private UserMapper userMapper;\n\n    @Test\n    public void testInsert() {\n        UserDO user = new UserDO().setUsername(UUID.randomUUID().toString())\n                .setPassword(\"nicai\").setCreateTime(new Date())\n                .setDeleted(0); // 一般情况下，是否删除，可以全局枚举下。\n        userMapper.insert(user);\n    }\n\n    @Test\n    public void testUpdateById() {\n        UserDO updateUser = new UserDO().setId(1)\n                .setPassword(\"wobucai\");\n        userMapper.updateById(updateUser);\n    }\n\n    @Test\n    public void testDeleteById() {\n        userMapper.deleteById(2);\n    }\n\n    @Test\n    public void testSelectById() {\n        userMapper.selectById(1);\n    }\n\n    @Test\n    public void testSelectByUsername() {\n        userMapper.selectByUsername(\"yunai\");\n    }\n\n    @Test\n    public void testSelectByIds() {\n        List<UserDO> users = userMapper.selectByIds(Arrays.asList(1, 3));\n        System.out.println(\"users：\" + users.size());\n    }\n\n    @Test\n    public void testSelectPageByCreateTime() {\n        IPage<UserDO> page = new Page<>(1, 10);\n        Date createTime = new Date(2018 - 1990, Calendar.FEBRUARY, 24); // 临时 Demo ，实际不建议这么写\n        page = userMapper.selectPageByCreateTime(page, createTime);\n        System.out.println(\"users：\" + page.getRecords().size());\n    }\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus-tenant/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-12-mybatis-plus-tenant</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis Plus 的自动化配置 -->\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus-boot-starter</artifactId>\n            <version>3.4.1</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>transmittable-thread-local</artifactId>\n            <version>2.12.2</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/Application.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis;\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@MapperScan(basePackages = \"cn.iocoder.springboot.lab12.mybatis.mapper\")\npublic class Application {\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/config/AsyncConfig.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.config;\n\nimport com.alibaba.ttl.TtlRunnable;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.scheduling.annotation.EnableAsync;\nimport org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;\n\n@Configuration\n@EnableAsync\npublic class AsyncConfig {\n\n    @Bean\n    public BeanPostProcessor executorBeanPostProcessor() {\n        return new BeanPostProcessor() {\n\n            @Override\n            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {\n                if (!(bean instanceof ThreadPoolTaskExecutor)) {\n                    return bean;\n                }\n                ThreadPoolTaskExecutor executor = (ThreadPoolTaskExecutor) bean;\n                executor.setTaskDecorator(TtlRunnable::get);\n                return executor;\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/config/MybatisPlusConfig.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.config;\n\nimport cn.iocoder.springboot.lab12.mybatis.context.TenantHolder;\nimport com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;\nimport com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;\nimport com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;\nimport net.sf.jsqlparser.expression.Expression;\nimport net.sf.jsqlparser.expression.LongValue;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class MybatisPlusConfig {\n\n    /**\n     * 新多租户插件配置,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存万一出现问题\n     */\n    @Bean\n    public MybatisPlusInterceptor mybatisPlusInterceptor() {\n        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();\n        interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {\n\n            @Override\n            public Expression getTenantId() {\n                Integer tenantId = TenantHolder.getTenantId();\n                return new LongValue(tenantId);\n            }\n\n            // 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件\n            @Override\n            public boolean ignoreTable(String tableName) {\n                return false;\n            }\n\n        }));\n        // 如果用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor\n        // 用了分页插件必须设置 MybatisConfiguration#useDeprecatedExecutor = false\n//        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());\n        return interceptor;\n    }\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/context/TenantHolder.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.context;\n\nimport com.alibaba.ttl.TransmittableThreadLocal;\n\npublic class TenantHolder {\n\n    private static final ThreadLocal<Integer> TENANT_ID = new TransmittableThreadLocal<>();\n//    private static final ThreadLocal<Integer> TENANT_ID = new ThreadLocal<>();\n//    private static final ThreadLocal<Integer> TENANT_ID = new InheritableThreadLocal<>();\n\n    public static void setTenantId(Integer tenantId) {\n        TENANT_ID.set(tenantId);\n    }\n\n    public static Integer getTenantId() {\n        return TENANT_ID.get();\n    }\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/core/TtlThreadPoolTaskExecutor.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.core;\n\nimport com.alibaba.ttl.TtlCallable;\nimport com.alibaba.ttl.TtlRunnable;\nimport org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;\nimport org.springframework.util.concurrent.ListenableFuture;\n\nimport java.util.Objects;\nimport java.util.concurrent.Callable;\nimport java.util.concurrent.Future;\n\n@Deprecated\npublic class TtlThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {\n\n    @Override\n    public void execute(Runnable task) {\n        super.execute(Objects.requireNonNull(TtlRunnable.get(task)));\n    }\n\n    @Override\n    public void execute(Runnable task, long startTimeout) {\n        super.execute(Objects.requireNonNull(TtlRunnable.get(task)), startTimeout);\n    }\n\n    @Override\n    public Future<?> submit(Runnable task) {\n        return super.submit(Objects.requireNonNull(TtlRunnable.get(task)));\n    }\n\n    @Override\n    public <T> Future<T> submit(Callable<T> task) {\n        return super.submit(Objects.requireNonNull(TtlCallable.get(task)));\n    }\n\n    @Override\n    public ListenableFuture<?> submitListenable(Runnable task) {\n        return super.submitListenable(Objects.requireNonNull(TtlRunnable.get(task)));\n    }\n\n    @Override\n    public <T> ListenableFuture<T> submitListenable(Callable<T> task) {\n        return super.submitListenable(Objects.requireNonNull(TtlCallable.get(task)));\n    }\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.dataobject;\n\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport com.baomidou.mybatisplus.annotation.TableName;\n\nimport java.util.Date;\n\n/**\n * 用户 DO\n */\n@TableName(value = \"users\")\npublic class UserDO {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码（明文）\n     *\n     * ps：生产环境下，千万不要明文噢\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n    /**\n     * 是否删除\n     */\n    @TableLogic\n    private Integer deleted;\n    /**\n     * 租户编号\n     */\n    private Integer tenantId;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    public UserDO setCreateTime(Date createTime) {\n        this.createTime = createTime;\n        return this;\n    }\n\n    public Integer getDeleted() {\n        return deleted;\n    }\n\n    public UserDO setDeleted(Integer deleted) {\n        this.deleted = deleted;\n        return this;\n    }\n\n    public Integer getTenantId() {\n        return tenantId;\n    }\n\n    public UserDO setTenantId(Integer tenantId) {\n        this.tenantId = tenantId;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/dataobject/UserProfileDO.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.dataobject;\n\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport com.baomidou.mybatisplus.annotation.TableName;\n\n/**\n * 用户拓展 DO\n */\n@TableName(value = \"user_profile\")\npublic class UserProfileDO {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n    /**\n     * 用户编号\n     */\n    private Integer userId;\n    /**\n     * 性别\n     */\n    private Integer gender;\n    /**\n     * 是否删除\n     */\n    @TableLogic\n    private Integer deleted;\n    /**\n     * 租户编号\n     */\n    private Integer tenantId;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserProfileDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserProfileDO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n    public Integer getDeleted() {\n        return deleted;\n    }\n\n    public UserProfileDO setDeleted(Integer deleted) {\n        this.deleted = deleted;\n        return this;\n    }\n\n    public Integer getTenantId() {\n        return tenantId;\n    }\n\n    public UserProfileDO setTenantId(Integer tenantId) {\n        this.tenantId = tenantId;\n        return this;\n    }\n\n    public Integer getUserId() {\n        return userId;\n    }\n\n    public UserProfileDO setUserId(Integer userId) {\n        this.userId = userId;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapper.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.mapper;\n\nimport cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO;\nimport cn.iocoder.springboot.lab12.mybatis.vo.UserDetailVO;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\n\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.List;\n\n@Repository\npublic interface UserMapper extends BaseMapper<UserDO> {\n\n    default UserDO selectByUsername(@Param(\"username\") String username) {\n        return selectOne(new QueryWrapper<UserDO>().eq(\"username\", username));\n    }\n\n    List<UserDO> selectByIds(@Param(\"ids\") Collection<Integer> ids);\n\n    default IPage<UserDO> selectPageByCreateTime(IPage<UserDO> page, @Param(\"createTime\") Date createTime) {\n        return selectPage(page,\n                new QueryWrapper<UserDO>().gt(\"create_time\", createTime)\n//                new QueryWrapper<UserDO>().like(\"username\", \"46683d9d\")\n        );\n    }\n\n    List<UserDetailVO> selectListA();\n\n    List<UserDetailVO> selectListB();\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserProfileMapper.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.mapper;\n\nimport cn.iocoder.springboot.lab12.mybatis.dataobject.UserProfileDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic interface UserProfileMapper extends BaseMapper<UserProfileDO> {\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/service/UserService.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.service;\n\nimport cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO;\nimport cn.iocoder.springboot.lab12.mybatis.mapper.UserMapper;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.scheduling.annotation.AsyncResult;\nimport org.springframework.stereotype.Service;\n\nimport javax.annotation.Resource;\nimport java.util.concurrent.Future;\n\n@Service\npublic class UserService {\n\n    private final Logger log = LoggerFactory.getLogger(UserService.class);\n\n    @Resource\n    private UserMapper userMapper;\n\n    @Async\n    public Future<UserDO> getUserAsync(Integer id) {\n        UserDO userDO = userMapper.selectById(id);\n        log.info(\"[getUserAsync][id({}) user({})]\", id, userDO);\n        return AsyncResult.forValue(userDO);\n    }\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/util/TtlExecutorsUtil.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.util;\n\nimport com.alibaba.ttl.spi.TtlEnhanced;\nimport com.alibaba.ttl.threadpool.agent.TtlAgent;\nimport org.springframework.lang.Nullable;\n\nimport java.util.concurrent.Executor;\n\n/**\n * {@link com.alibaba.ttl.threadpool.TtlExecutors} 工具类\n */\n@Deprecated\npublic class TtlExecutorsUtil {\n\n    public static Executor getTtlThreadPoolTaskExecutor(@Nullable Executor executor) {\n        if (TtlAgent.isTtlAgentLoaded() || null == executor || executor instanceof TtlEnhanced) {\n            return executor;\n        }\n//        return new ExecutorTtlWrapper(executor, true);\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/java/cn/iocoder/springboot/lab12/mybatis/vo/UserDetailVO.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.vo;\n\nimport com.baomidou.mybatisplus.annotation.TableLogic;\n\nimport java.util.Date;\n\npublic class UserDetailVO {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码（明文）\n     *\n     * ps：生产环境下，千万不要明文噢\n     */\n    private String password;\n    /**\n     * 性别\n     */\n    private Integer gender;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n    /**\n     * 是否删除\n     */\n    @TableLogic\n    private Integer deleted;\n    /**\n     * 租户编号\n     */\n    private Integer tenantId;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDetailVO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDetailVO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDetailVO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserDetailVO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    public UserDetailVO setCreateTime(Date createTime) {\n        this.createTime = createTime;\n        return this;\n    }\n\n    public Integer getDeleted() {\n        return deleted;\n    }\n\n    public UserDetailVO setDeleted(Integer deleted) {\n        this.deleted = deleted;\n        return this;\n    }\n\n    public Integer getTenantId() {\n        return tenantId;\n    }\n\n    public UserDetailVO setTenantId(Integer tenantId) {\n        this.tenantId = tenantId;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserDetailVO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                \", gender=\" + gender +\n                \", createTime=\" + createTime +\n                \", deleted=\" + deleted +\n                \", tenantId=\" + tenantId +\n                '}';\n    }\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/testb5f4?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password: 123456\n\n# mybatis-plus 配置内容\nmybatis-plus:\n  configuration:\n    map-underscore-to-camel-case: true # 虽然默认为 true ，但是还是显示去指定下。\n  global-config:\n    db-config:\n      id-type: auto # ID 主键自增\n      logic-delete-value: 1 # 逻辑已删除值(默认为 1)\n      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)\n  mapper-locations: classpath*:mapper/*.xml\n  type-aliases-package: cn.iocoder.springboot.lab12.mybatis.dataobject\n\n# logging\nlogging:\n  level:\n    # dao 开启 debug 模式 mybatis 输入 sql\n    cn:\n      iocoder:\n        springboot:\n          lab12:\n            mybatis:\n              mapper: debug\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/resources/mapper/UserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.springboot.lab12.mybatis.mapper.UserMapper\">\n\n    <sql id=\"FIELDS\">\n        id, username, password, create_time\n    </sql>\n\n    <select id=\"selectByIds\" resultType=\"UserDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM users\n        WHERE id IN\n            <foreach item=\"id\" collection=\"ids\" separator=\",\" open=\"(\" close=\")\" index=\"\">\n                #{id}\n            </foreach>\n    </select>\n\n    <select id=\"selectListA\" resultType=\"cn.iocoder.springboot.lab12.mybatis.vo.UserDetailVO\">\n        SELECT\n            u.id, u.username, u.password, u.create_time, p.gender\n        FROM users u\n        JOIN user_profile p\n        ON u.id = p.user_id\n    </select>\n\n    <select id=\"selectListB\" resultType=\"cn.iocoder.springboot.lab12.mybatis.vo.UserDetailVO\">\n        SELECT\n            u.id, u.username, u.password, u.create_time, p.gender\n        FROM users u, user_profile p\n        WHERE u.id = p.user_id\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus-tenant/src/main/resources/sql/users.sql",
    "content": "CREATE TABLE `users`\n(\n    `id`          int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',\n    `username`    varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',\n    `password`    varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码',\n    `create_time` datetime                        DEFAULT NULL COMMENT '创建时间',\n    `deleted`     bit(1)                          DEFAULT NULL COMMENT '是否删除。0-未删除；1-删除',\n    `tenant_id`   int(11) NOT NULL COMMENT '租户编号',\n    PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n\nCREATE TABLE `user_profile`\n(\n    `id`        int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',\n    `user_id`   int(11) NOT NULL COMMENT '用户编号',\n    `gender`    int(11) NOT NULL COMMENT '性别',\n    `deleted`   bit(1) DEFAULT NULL COMMENT '是否删除。0-未删除；1-删除',\n    `tenant_id` int(11) NOT NULL COMMENT '租户编号',\n    PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus-tenant/src/test/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapperTest.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.mapper;\n\nimport cn.iocoder.springboot.lab12.mybatis.Application;\nimport cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO;\nimport cn.iocoder.springboot.lab12.mybatis.dataobject.UserProfileDO;\nimport cn.iocoder.springboot.lab12.mybatis.vo.UserDetailVO;\nimport com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;\nimport com.baomidou.mybatisplus.core.metadata.IPage;\nimport com.baomidou.mybatisplus.extension.plugins.pagination.Page;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.*;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserMapperTest {\n\n    @Autowired\n    private UserMapper userMapper;\n    @Autowired\n    private UserProfileMapper userProfileMapper;\n\n    @Test\n    public void initTestData() {\n        // 清理数据\n        userMapper.delete(new QueryWrapper<>());\n        userProfileMapper.delete(new QueryWrapper<>());\n        // 插入一个用户\n        UserDO userDO = new UserDO().setUsername(UUID.randomUUID().toString())\n                .setPassword(\"nicai\").setCreateTime(new Date())\n                .setDeleted(0); // 一般情况下，是否删除，可以全局枚举下。\n        userMapper.insert(userDO);\n        // 插入该用户的拓展信息\n        UserProfileDO userProfileDO = new UserProfileDO();\n        userProfileDO.setUserId(userDO.getId());\n        userProfileDO.setGender(1);\n        userProfileDO.setTenantId(10); // TODO 全局写死\n        userProfileDO.setDeleted(0); // 一般情况下，是否删除，可以全局枚举下。\n        userProfileMapper.insert(userProfileDO);\n    }\n\n    @Test\n    public void testInsert() {\n        UserDO user = new UserDO().setUsername(UUID.randomUUID().toString())\n                .setPassword(\"nicai\").setCreateTime(new Date())\n                .setDeleted(0); // 一般情况下，是否删除，可以全局枚举下。\n        userMapper.insert(user);\n    }\n\n    @Test\n    public void testUpdateById() {\n        UserDO updateUser = new UserDO().setId(1)\n                .setPassword(\"wobucai\");\n        userMapper.updateById(updateUser);\n    }\n\n    @Test\n    public void testDeleteById() {\n        userMapper.deleteById(2);\n    }\n\n    @Test\n    public void testSelectById() {\n        userMapper.selectById(1);\n    }\n\n    @Test\n    public void testSelectByUsername() {\n        UserDO userDO = userMapper.selectByUsername(\"yunai\");\n        System.out.println(userDO);\n    }\n\n    @Test\n    public void testSelectByIds() {\n        List<UserDO> users = userMapper.selectByIds(Arrays.asList(1, 3));\n        System.out.println(\"users：\" + users.size());\n    }\n\n    @Test\n    public void testSelectPageByCreateTime() {\n        IPage<UserDO> page = new Page<>(1, 10);\n        Date createTime = new Date(2018 - 1990, Calendar.FEBRUARY, 24); // 临时 Demo ，实际不建议这么写\n        page = userMapper.selectPageByCreateTime(page, createTime);\n        System.out.println(\"users：\" + page.getRecords().size());\n    }\n\n    @Test\n    public void testSelectListA() {\n        List<UserDetailVO> list = userMapper.selectListA();\n        System.out.println(list);\n    }\n\n    @Test\n    public void testSelectListB() {\n        List<UserDetailVO> list = userMapper.selectListB();\n        System.out.println(list);\n    }\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-plus-tenant/src/test/java/cn/iocoder/springboot/lab12/mybatis/service/UserServiceTest.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.service;\n\nimport cn.iocoder.springboot.lab12.mybatis.Application;\nimport cn.iocoder.springboot.lab12.mybatis.context.TenantHolder;\nimport cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport javax.annotation.Resource;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\n\nimport static org.junit.Assert.*;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserServiceTest {\n\n    @Resource\n    private UserService userService;\n\n    @Test\n    public void testGetUserAsync() throws ExecutionException, InterruptedException {\n        TenantHolder.setTenantId(10); // TODO 芋艿：写死\n        Future<UserDO> future = userService.getUserAsync(9);\n        future.get();\n    }\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-tk/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-12-mybatis-tk</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.1</version>\n        </dependency>\n\n        <!-- 实现对 Mapper 的自动化配置-->\n        <dependency>\n            <groupId>tk.mybatis</groupId>\n            <artifactId>mapper-spring-boot-starter</artifactId>\n            <version>2.1.5</version>\n        </dependency>\n        <!-- 实现对 PageHelper 的自动化配置-->\n        <dependency>\n            <groupId>com.github.pagehelper</groupId>\n            <artifactId>pagehelper-spring-boot-starter</artifactId>\n            <version>1.2.5</version>\n            <exclusions>\n                <exclusion>\n                    <groupId>org.mybatis.spring.boot</groupId>\n                    <artifactId>mybatis-spring-boot-starter</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-tk/src/main/java/cn/iocoder/springboot/lab12/mybatis/Application.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport tk.mybatis.spring.annotation.MapperScan;\n\n@SpringBootApplication\n@MapperScan(basePackages = \"cn.iocoder.springboot.lab12.mybatis.mapper\") // 注意，要换成 tk 提供的 @MapperScan 注解\npublic class Application {\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-tk/src/main/java/cn/iocoder/springboot/lab12/mybatis/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.dataobject;\n\nimport javax.persistence.GeneratedValue;\nimport javax.persistence.GenerationType;\nimport javax.persistence.Id;\nimport javax.persistence.Table;\nimport java.util.Date;\n\n/**\n * 用户 DO\n */\n@Table(name = \"users\")\npublic class UserDO {\n\n    /**\n     * 用户编号\n     */\n    @Id // 表示该字段为主键 ID\n    @GeneratedValue(strategy = GenerationType.IDENTITY, generator = \"JDBC\") // strategy 设置使用数据库主键自增策略；generator 设置插入完成后，查询最后生成的 ID 填充到该属性中。\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码（明文）\n     *\n     * ps：生产环境下，千万不要明文噢\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    public UserDO setCreateTime(Date createTime) {\n        this.createTime = createTime;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-tk/src/main/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapper.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.mapper;\n\nimport cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO;\nimport cn.iocoder.springboot.lab12.mybatis.util.BaseMapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\nimport tk.mybatis.mapper.entity.Example;\n\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.List;\n\n@Repository\npublic interface UserMapper extends BaseMapper<UserDO> { // 继承的是，我们定义的 BaseMapper 接口\n\n    default UserDO selectByUsername(@Param(\"username\") String username) {\n        Example example = new Example(UserDO.class);\n        // 创建 Criteria 对象，设置 username 查询条件\n        example.createCriteria().andEqualTo(\"username\", username);\n        // 执行查询\n        return selectOneByExample(example);\n    }\n\n    List<UserDO> selectByIds(@Param(\"ids\") Collection<Integer> ids);\n\n    default List<UserDO> selectListByCreateTime(@Param(\"createTime\") Date createTime) {\n        Example example = new Example(UserDO.class);\n        // 创建 Criteria 对象，设置 create_time 查询条件\n        example.createCriteria().andGreaterThan(\"createTime\", createTime);\n        return selectByExample(example);\n    }\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-tk/src/main/java/cn/iocoder/springboot/lab12/mybatis/util/BaseMapper.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.util;\n\nimport tk.mybatis.mapper.common.Mapper;\nimport tk.mybatis.mapper.common.MySqlMapper;\n\npublic interface BaseMapper<T> extends Mapper<T>, MySqlMapper<T> {\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-tk/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    url: jdbc:mysql://47.112.193.81:3306/testb5f4?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: testb5f4\n    password: F4df4db0ed86@11\n\n# mybatis 配置内容\nmybatis:\n  config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径\n  mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址\n  type-aliases-package: cn.iocoder.springboot.lab12.mybatis.dataobject # 配置数据库实体包路径\n\n# mapper 配置内容\nmapper:\n  mappers: cn.iocoder.springboot.lab12.mybatis.util.BaseMapper # 设置基础 Mapper 接口\n  not-empty: true # 在 INSERT 和 UPDATE 操作时，是否会判断字段是否为空。即 <if test='xxx != null' />\n  identity: MYSQL\n\n# PageHelper 配置内容\n# 具体的参数作用，看 https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md\npagehelper:\n  helperDialect: mysql # 分页插件会自动检测当前的数据库链接，自动选择合适的分页方式。 你可以配置helperDialect属性来指定分页插件使用哪种方言。\n  reasonable: true # 分页合理化参数，默认值为false。当该参数设置为 true 时，pageNum<=0 时会查询第一页， pageNum>pages（超过总数时），会查询最后一页。默认false 时，直接根据参数进行查询。\n  supportMethodsArguments: true # 支持通过 Mapper 接口参数来传递分页参数，默认值false，分页插件会从查询方法的参数值中，自动根据上面 params 配置的字段中取值，查找到合适的值时就会自动分页。 使用方法可以参考测试代码中的 com.github.pagehelper.test.basic 包下的 ArgumentsMapTest 和 ArgumentsObjTest。\n\n# logging\nlogging:\n  level:\n    # dao 开启 debug 模式 mybatis 输入 sql\n    cn:\n      iocoder:\n        springboot:\n          lab12:\n            mybatis:\n              mapper: debug\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-tk/src/main/resources/mapper/UserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.springboot.lab12.mybatis.mapper.UserMapper\">\n\n    <sql id=\"FIELDS\">\n        id, username, password, create_time\n    </sql>\n\n    <select id=\"selectByIds\" resultType=\"UserDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM users\n        WHERE id IN\n            <foreach item=\"id\" collection=\"ids\" separator=\",\" open=\"(\" close=\")\" index=\"\">\n                #{id}\n            </foreach>\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-tk/src/main/resources/mybatis-config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE configuration PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n<configuration>\n\n    <settings>\n        <!-- 使用驼峰命名法转换字段。 -->\n        <setting name=\"mapUnderscoreToCamelCase\" value=\"true\"/>\n    </settings>\n\n    <typeAliases>\n        <typeAlias alias=\"Integer\" type=\"java.lang.Integer\"/>\n        <typeAlias alias=\"Long\" type=\"java.lang.Long\"/>\n        <typeAlias alias=\"HashMap\" type=\"java.util.HashMap\"/>\n        <typeAlias alias=\"LinkedHashMap\" type=\"java.util.LinkedHashMap\"/>\n        <typeAlias alias=\"ArrayList\" type=\"java.util.ArrayList\"/>\n        <typeAlias alias=\"LinkedList\" type=\"java.util.LinkedList\"/>\n    </typeAliases>\n\n</configuration>\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-tk/src/main/resources/sql/users.sql",
    "content": "CREATE TABLE `users` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',\n  `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',\n  `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码',\n  `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_username` (`username`)\n) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-tk/src/test/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapperTest.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.mapper;\n\nimport cn.iocoder.springboot.lab12.mybatis.Application;\nimport cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO;\nimport com.github.pagehelper.PageHelper;\nimport com.github.pagehelper.PageInfo;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.*;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserMapperTest {\n\n    @Autowired\n    private UserMapper userMapper;\n\n    @Test\n    public void testInsert() {\n        UserDO user = new UserDO().setUsername(UUID.randomUUID().toString())\n                .setPassword(\"nicai\").setCreateTime(new Date());\n        userMapper.insert(user);\n        System.out.println(user.getId());\n    }\n\n    @Test\n    public void testUpdateById() {\n        UserDO updateUser = new UserDO().setId(1)\n                .setPassword(\"wobucai\");\n        userMapper.updateByPrimaryKey(updateUser);\n    }\n\n    @Test\n    public void testDeleteById() {\n        userMapper.deleteByPrimaryKey(2);\n    }\n\n    @Test\n    public void testSelectById() {\n        userMapper.selectByPrimaryKey(1);\n    }\n\n    @Test\n    public void testSelectByUsername() {\n        userMapper.selectByUsername(\"yunai\");\n    }\n\n    @Test\n    public void testSelectByIds() {\n        List<UserDO> users = userMapper.selectByIds(Arrays.asList(1, 3));\n        System.out.println(\"users：\" + users.size());\n    }\n\n    @Test // 更多使用，可以参考 https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md\n    public void testSelectPageByCreateTime() {\n        // 设置分页\n        PageHelper.startPage(1, 10);\n        Date createTime = new Date(2018 - 1990, Calendar.FEBRUARY, 24); // 临时 Demo ，实际不建议这么写\n        // 执行列表查询\n        // PageHelper 会自动发起分页的数量查询，设置到 PageHelper 中\n        List<UserDO> users = userMapper.selectListByCreateTime(createTime); // 实际返回的是 com.github.pagehelper.Page 代理对象\n        // 转换成 PageInfo 对象，并输出分页\n        PageInfo<UserDO> page = new PageInfo<>(users);\n        System.out.println(page.getTotal());\n    }\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-xml/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-12-mybatis-xml</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.1</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-xml/src/main/java/cn/iocoder/springboot/lab12/mybatis/Application.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis;\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@MapperScan(basePackages = \"cn.iocoder.springboot.lab12.mybatis.mapper\")\npublic class Application {\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-xml/src/main/java/cn/iocoder/springboot/lab12/mybatis/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.dataobject;\n\nimport java.util.Date;\n\n/**\n * 用户 DO\n */\npublic class UserDO {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码（明文）\n     *\n     * ps：生产环境下，千万不要明文噢\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    public UserDO setCreateTime(Date createTime) {\n        this.createTime = createTime;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-xml/src/main/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapper.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.mapper;\n\nimport cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\n\nimport java.util.Collection;\nimport java.util.List;\n\n@Repository\npublic interface UserMapper {\n\n    int insert(UserDO user);\n\n    int updateById(UserDO user);\n\n    int deleteById(@Param(\"id\") Integer id); // 生产请使用标记删除，除非有点想不开，嘿嘿。\n\n    UserDO selectById(@Param(\"id\") Integer id);\n\n    UserDO selectByUsername(@Param(\"username\") String username);\n\n    List<UserDO> selectByIds(@Param(\"ids\")Collection<Integer> ids);\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-xml/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    url: jdbc:mysql://47.112.193.81:3306/testb5f4?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: testb5f4\n    password: F4df4db0ed86@11\n\n# mybatis 配置内容\nmybatis:\n  config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径\n  mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址\n  type-aliases-package: cn.iocoder.springboot.lab12.mybatis.dataobject # 配置数据库实体包路径\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-xml/src/main/resources/mapper/UserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.springboot.lab12.mybatis.mapper.UserMapper\">\n\n    <sql id=\"FIELDS\">\n        id, username, password, create_time\n    </sql>\n\n    <insert id=\"insert\" parameterType=\"UserDO\" useGeneratedKeys=\"true\" keyProperty=\"id\">\n        INSERT INTO users (\n          username, password, create_time\n        ) VALUES (\n          #{username}, #{password}, #{createTime}\n        )\n    </insert>\n\n    <update id=\"updateById\" parameterType=\"UserDO\">\n        UPDATE users\n        <set>\n            <if test=\"username != null\">\n                , username = #{username}\n            </if>\n            <if test=\"password != null\">\n                , password = #{password}\n            </if>\n        </set>\n        WHERE id = #{id}\n    </update>\n\n    <delete id=\"deleteById\" parameterType=\"Integer\">\n        DELETE FROM users\n        WHERE id = #{id}\n    </delete>\n\n    <select id=\"selectById\" parameterType=\"Integer\" resultType=\"UserDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM users\n        WHERE id = #{id}\n    </select>\n\n    <select id=\"selectByUsername\" parameterType=\"String\" resultType=\"UserDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM users\n        WHERE username = #{username}\n        LIMIT 1\n    </select>\n\n    <select id=\"selectByIds\" resultType=\"UserDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM users\n        WHERE id IN\n            <foreach item=\"id\" collection=\"ids\" separator=\",\" open=\"(\" close=\")\" index=\"\">\n                #{id}\n            </foreach>\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-xml/src/main/resources/mybatis-config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE configuration PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n<configuration>\n\n    <settings>\n        <!-- 使用驼峰命名法转换字段。 -->\n        <setting name=\"mapUnderscoreToCamelCase\" value=\"true\"/>\n    </settings>\n\n    <typeAliases>\n        <typeAlias alias=\"Integer\" type=\"java.lang.Integer\"/>\n        <typeAlias alias=\"Long\" type=\"java.lang.Long\"/>\n        <typeAlias alias=\"HashMap\" type=\"java.util.HashMap\"/>\n        <typeAlias alias=\"LinkedHashMap\" type=\"java.util.LinkedHashMap\"/>\n        <typeAlias alias=\"ArrayList\" type=\"java.util.ArrayList\"/>\n        <typeAlias alias=\"LinkedList\" type=\"java.util.LinkedList\"/>\n    </typeAliases>\n\n</configuration>\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-xml/src/main/resources/sql/users.sql",
    "content": "CREATE TABLE `users` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',\n  `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',\n  `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码',\n  `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_username` (`username`)\n) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n"
  },
  {
    "path": "lab-12-mybatis/lab-12-mybatis-xml/src/test/java/cn/iocoder/springboot/lab12/mybatis/mapper/UserMapperTest.java",
    "content": "package cn.iocoder.springboot.lab12.mybatis.mapper;\n\nimport cn.iocoder.springboot.lab12.mybatis.Application;\nimport cn.iocoder.springboot.lab12.mybatis.dataobject.UserDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.UUID;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserMapperTest {\n\n    @Autowired\n    private UserMapper userMapper;\n\n    @Test\n    public void testInsert() {\n        UserDO user = new UserDO().setUsername(UUID.randomUUID().toString())\n                .setPassword(\"nicai\").setCreateTime(new Date());\n        userMapper.insert(user);\n    }\n\n    @Test\n    public void testUpdateById() {\n        UserDO updateUser = new UserDO().setId(1)\n                .setPassword(\"wobucai\");\n        userMapper.updateById(updateUser);\n    }\n\n    @Test\n    public void testDeleteById() {\n        userMapper.deleteById(2);\n    }\n\n    @Test\n    public void testSelectById() {\n        userMapper.selectById(1);\n    }\n\n    @Test\n    public void testSelectByUsername() {\n        userMapper.selectByUsername(\"yunai\");\n    }\n\n    @Test\n    public void testSelectByIds() {\n        List<UserDO> users = userMapper.selectByIds(Arrays.asList(1, 3));\n        System.out.println(\"users：\" + users.size());\n    }\n\n}\n"
  },
  {
    "path": "lab-12-mybatis/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-12-mybatis</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-12-mybatis-xml</module>\n        <module>lab-12-mybatis-annotation</module>\n        <module>lab-12-mybatis-plus</module>\n        <module>lab-12-mybatis-tk</module>\n        <module>lab-12-mybatis-plus-tenant</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-12-mybatis/《芋道 Spring Boot MyBatis 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/MyBatis/?github>\n"
  },
  {
    "path": "lab-13-spring-data-jpa/lab-13-jpa/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-13-jpa</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 Spring Data JPA 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-jpa</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-13-spring-data-jpa/lab-13-jpa/src/main/java/cn/iocoder/springboot/lab13/jpa/Application.java",
    "content": "package cn.iocoder.springboot.lab13.jpa;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n}\n"
  },
  {
    "path": "lab-13-spring-data-jpa/lab-13-jpa/src/main/java/cn/iocoder/springboot/lab13/jpa/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab13.jpa.dataobject;\n\nimport javax.persistence.*;\nimport java.util.Date;\n\n/**\n * 用户 DO\n */\n@Entity\n@Table(name = \"users\")\npublic class UserDO {\n\n    /**\n     * 用户编号\n     */\n    @Id\n    @GeneratedValue(strategy = GenerationType.IDENTITY,  // strategy 设置使用数据库主键自增策略；\n            generator = \"JDBC\") // generator 设置插入完成后，查询最后生成的 ID 填充到该属性中。\n    private Integer id;\n    /**\n     * 账号\n     */\n    @Column(nullable = false)\n    private String username;\n    /**\n     * 密码（明文）\n     *\n     * ps：生产环境下，千万不要明文噢\n     */\n    @Column(nullable = false)\n    private String password;\n    /**\n     * 创建时间\n     */\n    @Column(name = \"create_time\", nullable = false)\n    private Date createTime;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    public UserDO setCreateTime(Date createTime) {\n        this.createTime = createTime;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-13-spring-data-jpa/lab-13-jpa/src/main/java/cn/iocoder/springboot/lab13/jpa/repository/UserRepository01.java",
    "content": "package cn.iocoder.springboot.lab13.jpa.repository;\n\nimport cn.iocoder.springboot.lab13.jpa.dataobject.UserDO;\nimport org.springframework.data.repository.CrudRepository;\n\npublic interface UserRepository01 extends CrudRepository<UserDO, Integer> {\n\n}\n"
  },
  {
    "path": "lab-13-spring-data-jpa/lab-13-jpa/src/main/java/cn/iocoder/springboot/lab13/jpa/repository/UserRepository02.java",
    "content": "package cn.iocoder.springboot.lab13.jpa.repository;\n\nimport cn.iocoder.springboot.lab13.jpa.dataobject.UserDO;\nimport org.springframework.data.repository.PagingAndSortingRepository;\n\npublic interface UserRepository02 extends PagingAndSortingRepository<UserDO, Integer> {\n\n}\n"
  },
  {
    "path": "lab-13-spring-data-jpa/lab-13-jpa/src/main/java/cn/iocoder/springboot/lab13/jpa/repository/UserRepository03.java",
    "content": "package cn.iocoder.springboot.lab13.jpa.repository;\n\nimport cn.iocoder.springboot.lab13.jpa.dataobject.UserDO;\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.repository.PagingAndSortingRepository;\n\nimport java.util.Date;\n\npublic interface UserRepository03 extends PagingAndSortingRepository<UserDO, Integer> {\n\n    UserDO findByUsername(String username);\n\n    Page<UserDO> findByCreateTimeAfter(Date createTime, Pageable pageable);\n\n}\n"
  },
  {
    "path": "lab-13-spring-data-jpa/lab-13-jpa/src/main/java/cn/iocoder/springboot/lab13/jpa/repository/UserRepository04.java",
    "content": "package cn.iocoder.springboot.lab13.jpa.repository;\n\nimport cn.iocoder.springboot.lab13.jpa.dataobject.UserDO;\nimport org.springframework.data.jpa.repository.Modifying;\nimport org.springframework.data.jpa.repository.Query;\nimport org.springframework.data.repository.PagingAndSortingRepository;\nimport org.springframework.data.repository.query.Param;\n\npublic interface UserRepository04 extends PagingAndSortingRepository<UserDO, Integer> {\n\n    @Query(\"SELECT u FROM UserDO u WHERE u.username = ?1\")\n    UserDO findByUsername01(String username);\n\n    @Query(\"SELECT u FROM UserDO u WHERE u.username = :username\")\n    UserDO findByUsername02(@Param(\"username\") String username);\n\n    @Query(value = \"SELECT * FROM users u WHERE u.username = :username\", nativeQuery = true)\n    UserDO findByUsername03(@Param(\"username\") String username);\n\n    @Query(\"UPDATE UserDO  u SET u.username = :username WHERE u.id = :id\")\n    @Modifying\n    int updateUsernameById(Integer id, String username);\n\n}\n"
  },
  {
    "path": "lab-13-spring-data-jpa/lab-13-jpa/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    url: jdbc:mysql://47.112.193.81:3306/testb5f4?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: testb5f4\n    password: F4df4db0ed86@11\n  # JPA 配置内容，对应 JpaProperties 类\n  jpa:\n    show-sql: true # 打印 SQL 。生产环境，建议关闭\n    # Hibernate 配置内容，对应 HibernateProperties 类\n    hibernate:\n      ddl-auto: none\n"
  },
  {
    "path": "lab-13-spring-data-jpa/lab-13-jpa/src/test/java/cn/iocoder/springboot/lab13/jpa/repository/UserRepository01Test.java",
    "content": "package cn.iocoder.springboot.lab13.jpa.repository;\n\nimport cn.iocoder.springboot.lab13.jpa.dataobject.UserDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.util.Assert;\n\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.Optional;\nimport java.util.UUID;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class UserRepository01Test {\n\n    @Autowired\n    private UserRepository01 userRepository;\n\n    @Test // 插入一条记录\n    public void testSave() {\n        UserDO user = new UserDO().setUsername(UUID.randomUUID().toString())\n                .setPassword(\"nicai\").setCreateTime(new Date());\n        userRepository.save(user);\n    }\n\n    @Test // 更新一条记录\n    public void testUpdate() {\n        // 先查询一条记录\n        Optional<UserDO> userDO = userRepository.findById(1);\n        Assert.isTrue(userDO.isPresent(), \"记录不能为空\");\n        // 更新一条记录\n        UserDO updateUser = userDO.get();\n        updateUser.setPassword(\"yudaoyuanma\");\n        userRepository.save(updateUser);\n    }\n\n    @Test // 根据 ID 编号，删除一条记录\n    public void testDelete() {\n        userRepository.deleteById(2);\n    }\n\n    @Test // 根据 ID 编号，查询一条记录\n    public void testSelectById() {\n        Optional<UserDO> userDO = userRepository.findById(1);\n        System.out.println(userDO.get());\n    }\n\n    @Test // 根据 ID 编号数组，查询多条记录\n    public void testSelectByIds() {\n        Iterable<UserDO> users = userRepository.findAllById(Arrays.asList(1, 4));\n        users.forEach(System.out::println);\n    }\n\n}\n"
  },
  {
    "path": "lab-13-spring-data-jpa/lab-13-jpa/src/test/java/cn/iocoder/springboot/lab13/jpa/repository/UserRepository02Test.java",
    "content": "package cn.iocoder.springboot.lab13.jpa.repository;\n\nimport cn.iocoder.springboot.lab13.jpa.dataobject.UserDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.PageRequest;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class UserRepository02Test {\n\n    @Autowired\n    private UserRepository02 userRepository;\n\n    @Test // 排序\n    public void testFindAll() {\n        // 创建排序条件\n        Sort sort = new Sort(Sort.Direction.DESC, \"id\");\n        // 执行排序操作\n        Iterable<UserDO> iterable = userRepository.findAll(sort);\n        // 打印\n        iterable.forEach(System.out::println);\n    }\n\n    @Test // 分页\n    public void testFindPage() {\n        // 创建排序条件\n        Sort sort = new Sort(Sort.Direction.DESC, \"id\");\n        // 创建分页条件\n        Pageable pageable = PageRequest.of(1, 10, sort);\n        // 执行分页操作\n        Page<UserDO> page = userRepository.findAll(pageable);\n        // 打印\n        System.out.println(page.getTotalElements());\n        System.out.println(page.getTotalPages());\n    }\n\n}\n"
  },
  {
    "path": "lab-13-spring-data-jpa/lab-13-jpa/src/test/java/cn/iocoder/springboot/lab13/jpa/repository/UserRepository03Test.java",
    "content": "package cn.iocoder.springboot.lab13.jpa.repository;\n\nimport cn.iocoder.springboot.lab13.jpa.dataobject.UserDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.PageRequest;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.Calendar;\nimport java.util.Date;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class UserRepository03Test {\n\n    @Autowired\n    private UserRepository03 userRepository;\n\n    @Test\n    public void testFindByUsername() {\n        UserDO user = userRepository.findByUsername(\"yunai\");\n        System.out.println(user);\n    }\n\n    @Test\n    public void testFindByCreateTimeAfter() {\n        // 创建分页条件\n        Pageable pageable = PageRequest.of(1, 10);\n        // 执行分页操作\n        Date createTime = new Date(2018 - 1990, Calendar.FEBRUARY, 24); // 临时 Demo ，实际不建议这么写\n        Page<UserDO> page = userRepository.findByCreateTimeAfter(createTime, pageable);\n        // 打印\n        System.out.println(page.getTotalElements());\n        System.out.println(page.getTotalPages());\n    }\n\n}\n"
  },
  {
    "path": "lab-13-spring-data-jpa/lab-13-jpa/src/test/java/cn/iocoder/springboot/lab13/jpa/repository/UserRepository04Test.java",
    "content": "package cn.iocoder.springboot.lab13.jpa.repository;\n\nimport cn.iocoder.springboot.lab13.jpa.dataobject.UserDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport javax.transaction.Transactional;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class UserRepository04Test {\n\n    @Autowired\n    private UserRepository04 userRepository;\n\n    @Test\n    public void testFindIdByUsername01() {\n        UserDO user = userRepository.findByUsername01(\"yunai\");\n        System.out.println(user);\n    }\n\n    @Test\n    public void testFindIdByUsername02() {\n        UserDO user = userRepository.findByUsername02(\"yunai\");\n        System.out.println(user);\n    }\n\n    @Test\n    public void testFindIdByUsername03() {\n        UserDO user = userRepository.findByUsername03(\"yunai\");\n        System.out.println(user);\n    }\n\n    @Test\n    // 更新操作，需要在事务中。\n    // 在单元测试中，事务默认回滚，所以胖友可能怎么测试，事务都不更新。\n    @Transactional\n    public void testUpdateUsernameById() {\n        userRepository.updateUsernameById(5, \"yudaoyuanma\");\n    }\n\n}\n"
  },
  {
    "path": "lab-13-spring-data-jpa/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-13-spring-data-jpa</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-13-jpa</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-13-spring-data-jpa/《芋道 Spring Boot JPA 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/JPA/?github>\n"
  },
  {
    "path": "lab-14-spring-jdbc-template/lab-14-jdbctemplate/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-14-jdbctemplate</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-14-spring-jdbc-template/lab-14-jdbctemplate/src/main/java/cn/iocoder/springboot/lab14/jdbctemplate/Application.java",
    "content": "package cn.iocoder.springboot.lab14.jdbctemplate;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n}\n"
  },
  {
    "path": "lab-14-spring-jdbc-template/lab-14-jdbctemplate/src/main/java/cn/iocoder/springboot/lab14/jdbctemplate/dao/UserDao.java",
    "content": "package cn.iocoder.springboot.lab14.jdbctemplate.dao;\n\nimport cn.iocoder.springboot.lab14.jdbctemplate.dataobject.UserDO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.BeanPropertyRowMapper;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.PreparedStatementCreatorFactory;\nimport org.springframework.jdbc.core.SqlParameter;\nimport org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;\nimport org.springframework.jdbc.core.simple.SimpleJdbcInsert;\nimport org.springframework.jdbc.support.GeneratedKeyHolder;\nimport org.springframework.jdbc.support.KeyHolder;\nimport org.springframework.stereotype.Repository;\n\nimport java.sql.Types;\nimport java.util.Arrays;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n@Repository\npublic class UserDao {\n\n    /**\n     * 声明 INSERT 操作的 PreparedStatementCreatorFactory 对象\n     */\n    private static final PreparedStatementCreatorFactory INSERT_PREPARED_STATEMENT_CREATOR_FACTORY\n            = new PreparedStatementCreatorFactory(\"INSERT INTO users(username, password, create_time) VALUES(?, ?, ?)\");\n\n    static {\n        // 设置返回主键\n        INSERT_PREPARED_STATEMENT_CREATOR_FACTORY.setReturnGeneratedKeys(true);\n        INSERT_PREPARED_STATEMENT_CREATOR_FACTORY.setGeneratedKeysColumnNames(\"id\");\n        // 设置每个占位符的类型\n        INSERT_PREPARED_STATEMENT_CREATOR_FACTORY.addParameter(new SqlParameter(Types.VARCHAR));\n        INSERT_PREPARED_STATEMENT_CREATOR_FACTORY.addParameter(new SqlParameter(Types.VARCHAR));\n        INSERT_PREPARED_STATEMENT_CREATOR_FACTORY.addParameter(new SqlParameter(Types.TIMESTAMP));\n    }\n\n    @Autowired\n    private JdbcTemplate template;\n\n    /**\n     * 使用 PreparedStatementCreator 实现插入数据\n     *\n     * @param entity 实体\n     * @return 影响行数\n     */\n    public int insert(UserDO entity) {\n        // 创建 KeyHolder 对象，设置返回的主键 ID\n        KeyHolder keyHolder = new GeneratedKeyHolder();\n        // 执行插入操作\n        int updateCounts = template.update(INSERT_PREPARED_STATEMENT_CREATOR_FACTORY.newPreparedStatementCreator(\n                Arrays.asList(entity.getUsername(), entity.getPassword(), entity.getCreateTime())\n        ), keyHolder);\n        // 设置 ID 主键到 entity 实体中\n        if (keyHolder.getKey() != null) {\n            entity.setId(keyHolder.getKey().intValue());\n        }\n        // 返回影响行数\n        return updateCounts;\n    }\n\n    /**\n     * 使用 SimpleJdbcInsert 实现插入数据\n     *\n     * @param entity 实体\n     * @return 影响行数\n     */\n    public int insert0(UserDO entity) {\n        // 创建 SimpleJdbcInsert 对象\n        SimpleJdbcInsert insertOp = new SimpleJdbcInsert(template);\n        insertOp.setTableName(\"users\");\n        insertOp.setColumnNames(Arrays.asList(\"username\", \"password\", \"create_time\"));\n        insertOp.setGeneratedKeyName(\"id\");\n        // 拼接参数\n        Map<String, Object> params = new HashMap<>();\n        params.put(\"username\", entity.getUsername());\n        params.put(\"password\", entity.getPassword());\n        params.put(\"create_time\", entity.getCreateTime());\n        // 执行插入操作\n        Number id = insertOp.executeAndReturnKey(params);\n        // 设置 ID 主键到 entity 实体中\n        entity.setId(id.intValue());\n        // 返回影响行数\n        return 1;\n    }\n\n    public int updateById(UserDO entity) {\n        // JdbcTemplate 生成更新的动态 SQL 不是很方便，需要自己二次封装。类似 SimpleJdbcInsert 对象\n        return template.update(\"UPDATE users SET password = ? WHERE id = ?\", entity.getPassword(),\n                entity.getId());\n    }\n\n    public int deleteById(Integer id) {\n        return template.update(\"DELETE FROM users WHERE id = ?\", id);\n    }\n\n    public UserDO selectById(Integer id) {\n        return template.queryForObject(\"SELECT id, username, password, create_time FROM users WHERE id = ?\",\n                new BeanPropertyRowMapper<>(UserDO.class), // 结果转换成对应的对象\n                id);\n    }\n\n    public UserDO selectByUsername(String username) {\n        return template.queryForObject(\"SELECT id, username, password, create_time FROM users WHERE username = ? LIMIT 1\",\n                new BeanPropertyRowMapper<>(UserDO.class), // 结果转换成对应的对象\n                username);\n    }\n\n    public List<UserDO> selectByIds(List<Integer> ids) {\n        // 创建 NamedParameterJdbcTemplate 对象\n        NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(template);\n        // 拼接参数\n        Map<String, Object> params = new HashMap<>();\n        params.put(\"ids\", ids);\n        // 执行查询\n        return namedParameterJdbcTemplate.query(\n                \"SELECT id, username, password, create_time FROM users WHERE id IN (:ids)\", // 使用 :ids 作为占位服务\n                params,\n                new BeanPropertyRowMapper<>(UserDO.class) // 结果转换成对应的对象\n        );\n    }\n\n}\n"
  },
  {
    "path": "lab-14-spring-jdbc-template/lab-14-jdbctemplate/src/main/java/cn/iocoder/springboot/lab14/jdbctemplate/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab14.jdbctemplate.dataobject;\n\nimport java.util.Date;\n\n/**\n * 用户 DO\n */\npublic class UserDO {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码（明文）\n     *\n     * ps：生产环境下，千万不要明文噢\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    public UserDO setCreateTime(Date createTime) {\n        this.createTime = createTime;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                \", createTime=\" + createTime +\n                '}';\n    }\n}\n"
  },
  {
    "path": "lab-14-spring-jdbc-template/lab-14-jdbctemplate/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    url: jdbc:mysql://47.112.193.81:3306/testb5f4?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: testb5f4\n    password: F4df4db0ed86@11\n"
  },
  {
    "path": "lab-14-spring-jdbc-template/lab-14-jdbctemplate/src/test/java/cn/iocoder/springboot/lab14/jdbctemplate/dao/UserDaoTest.java",
    "content": "package cn.iocoder.springboot.lab14.jdbctemplate.dao;\n\nimport cn.iocoder.springboot.lab14.jdbctemplate.Application;\nimport cn.iocoder.springboot.lab14.jdbctemplate.dataobject.UserDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\nimport java.util.UUID;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserDaoTest {\n\n    @Autowired\n    private UserDao userDao;\n\n    @Test\n    public void testInsert() {\n        UserDO user = new UserDO().setUsername(UUID.randomUUID().toString())\n                .setPassword(\"nicai\").setCreateTime(new Date());\n        userDao.insert(user);\n        System.out.println(user);\n    }\n\n    @Test\n    public void testInsert0() {\n        UserDO user = new UserDO().setUsername(UUID.randomUUID().toString())\n                .setPassword(\"nicai\").setCreateTime(new Date());\n        userDao.insert0(user);\n        System.out.println(user);\n    }\n\n    @Test\n    public void testUpdateById() {\n        UserDO updateUser = new UserDO().setId(1)\n                .setPassword(\"wobucai\");\n        userDao.updateById(updateUser);\n    }\n\n    @Test\n    public void testDeleteById() {\n        userDao.deleteById(2);\n    }\n\n    @Test\n    public void testSelectById() {\n        UserDO user = userDao.selectById(1);\n        System.out.println(user);\n    }\n\n    @Test\n    public void testSelectByUsername() {\n        UserDO user = userDao.selectByUsername(\"yunai\");\n        System.out.println(user);\n    }\n\n    @Test\n    public void testSelectByIds() {\n        List<UserDO> users = userDao.selectByIds(Arrays.asList(1, 5));\n        System.out.println(\"users：\" + users.size());\n    }\n\n}\n"
  },
  {
    "path": "lab-14-spring-jdbc-template/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-14-spring-jdbc-template</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-14-jdbctemplate</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-14-spring-jdbc-template/《芋道 Spring Boot JdbcTemplate 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/JdbcTemplate/?github>\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-elasticsearch/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-15-spring-data-elasticsearch</artifactId>\n\n    <dependencies>\n        <!-- 自动化配置 Spring Data Elasticsearch -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-elasticsearch/src/main/java/cn/iocoder/springboot/lab15/springdataelasticsearch/Application.java",
    "content": "package cn.iocoder.springboot.lab15.springdataelasticsearch;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n}\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-elasticsearch/src/main/java/cn/iocoder/springboot/lab15/springdataelasticsearch/bo/ProductConditionBO.java",
    "content": "package cn.iocoder.springboot.lab15.springdataelasticsearch.bo;\n\nimport java.util.List;\n\n/**\n * 商品搜索条件返回 BO\n */\npublic class ProductConditionBO {\n\n    /**\n     * 商品分类数组\n     */\n    private List<Category> categories;\n\n    public static class Category {\n\n        /**\n         * 分类编号\n         */\n        private Integer id;\n        /**\n         * 分类名称\n         */\n        private String name;\n\n        public Integer getId() {\n            return id;\n        }\n\n        public Category setId(Integer id) {\n            this.id = id;\n            return this;\n        }\n\n        public String getName() {\n            return name;\n        }\n\n        public Category setName(String name) {\n            this.name = name;\n            return this;\n        }\n\n    }\n\n    public List<Category> getCategories() {\n        return categories;\n    }\n\n    public ProductConditionBO setCategories(List<Category> categories) {\n        this.categories = categories;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-elasticsearch/src/main/java/cn/iocoder/springboot/lab15/springdataelasticsearch/constant/FieldAnalyzer.java",
    "content": "package cn.iocoder.springboot.lab15.springdataelasticsearch.constant;\n\n/**\n * ES 字段分析器的枚举类\n *\n * 关于 IK 分词，文章 https://blog.csdn.net/xsdxs/article/details/72853288 不错。\n * 目前项目使用的 ES 版本是 6.7.1 ，可以在 https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-7-1 下载。\n * 如果不知道怎么安装 ES ，可以看 https://blog.csdn.net/chengyuqiang/article/details/78837712 简单。\n */\npublic class FieldAnalyzer {\n\n    /**\n     * IK 最大化分词\n     *\n     * 会将文本做最细粒度的拆分\n     */\n    public static final String IK_MAX_WORD = \"ik_max_word\";\n\n    /**\n     * IK 智能分词\n     *\n     * 会做最粗粒度的拆分\n     */\n    public static final String IK_SMART = \"ik_smart\";\n\n}\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-elasticsearch/src/main/java/cn/iocoder/springboot/lab15/springdataelasticsearch/dataobject/ESProductDO.java",
    "content": "package cn.iocoder.springboot.lab15.springdataelasticsearch.dataobject;\n\nimport cn.iocoder.springboot.lab15.springdataelasticsearch.constant.FieldAnalyzer;\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.elasticsearch.annotations.Document;\nimport org.springframework.data.elasticsearch.annotations.Field;\nimport org.springframework.data.elasticsearch.annotations.FieldType;\n\n@Document(indexName = \"product\", // 索引名\n        type = \"product\", // 类型。未来的版本即将废弃\n        shards = 1, // 默认索引分区数\n        replicas = 0, // 每个分区的备份数\n        refreshInterval = \"-1\" // 刷新间隔\n)\npublic class ESProductDO {\n\n    /**\n     * ID 主键\n     */\n    @Id\n    private Integer id;\n\n    /**\n     * SPU 名字\n     */\n    @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)\n    private String name;\n    /**\n     * 卖点\n     */\n    @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)\n    private String sellPoint;\n    /**\n     * 描述\n     */\n    @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)\n    private String description;\n    /**\n     * 分类编号\n     */\n    private Integer cid;\n    /**\n     * 分类名\n     */\n    @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)\n    private String categoryName;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public ESProductDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public ESProductDO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public String getSellPoint() {\n        return sellPoint;\n    }\n\n    public ESProductDO setSellPoint(String sellPoint) {\n        this.sellPoint = sellPoint;\n        return this;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public ESProductDO setDescription(String description) {\n        this.description = description;\n        return this;\n    }\n\n    public Integer getCid() {\n        return cid;\n    }\n\n    public ESProductDO setCid(Integer cid) {\n        this.cid = cid;\n        return this;\n    }\n\n    public String getCategoryName() {\n        return categoryName;\n    }\n\n    public ESProductDO setCategoryName(String categoryName) {\n        this.categoryName = categoryName;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ProductDO{\" +\n                \"id=\" + id +\n                \", name='\" + name + '\\'' +\n                \", sellPoint='\" + sellPoint + '\\'' +\n                \", description='\" + description + '\\'' +\n                \", cid=\" + cid +\n                \", categoryName='\" + categoryName + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-elasticsearch/src/main/java/cn/iocoder/springboot/lab15/springdataelasticsearch/repository/ProductRepository.java",
    "content": "package cn.iocoder.springboot.lab15.springdataelasticsearch.repository;\n\nimport cn.iocoder.springboot.lab15.springdataelasticsearch.dataobject.ESProductDO;\nimport org.springframework.data.elasticsearch.repository.ElasticsearchRepository;\n\npublic interface ProductRepository extends ElasticsearchRepository<ESProductDO, Integer> {\n\n}\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-elasticsearch/src/main/resources/application.yaml",
    "content": "spring:\n  data:\n    # Elasticsearch 配置项\n    elasticsearch:\n      cluster-name: elasticsearch # 集群名\n      cluster-nodes: 127.0.0.1:9300 # 集群节点\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-elasticsearch/src/test/java/cn/iocoder/springboot/lab15/springdataelasticsearch/repository/ProductRepository04Test.java",
    "content": "package cn.iocoder.springboot.lab15.springdataelasticsearch.repository;\n\nimport cn.iocoder.springboot.lab15.springdataelasticsearch.Application;\nimport cn.iocoder.springboot.lab15.springdataelasticsearch.bo.ProductConditionBO;\nimport org.elasticsearch.index.query.QueryBuilders;\nimport org.elasticsearch.search.aggregations.Aggregation;\nimport org.elasticsearch.search.aggregations.AggregationBuilders;\nimport org.elasticsearch.search.aggregations.bucket.terms.LongTerms;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.data.elasticsearch.core.ElasticsearchTemplate;\nimport org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.ArrayList;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class ProductRepository04Test {\n\n    @Autowired\n    private ElasticsearchTemplate elasticsearchTemplate;\n\n    @Test\n    public void test() {\n        // 创建 ES 搜索条件\n        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder()\n                .withIndices(\"product\");\n        // 筛选\n        nativeSearchQueryBuilder.withQuery(QueryBuilders.multiMatchQuery(\"芋道\",\n                \"name\", \"sellPoint\", \"categoryName\"));\n        // 聚合\n        nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms(\"cids\").field(\"cid\")); // 商品分类\n        // 执行查询\n        ProductConditionBO condition = elasticsearchTemplate.query(nativeSearchQueryBuilder.build(), response -> {\n            ProductConditionBO result = new ProductConditionBO();\n            // categoryIds 聚合\n            Aggregation categoryIdsAggregation = response.getAggregations().get(\"cids\");\n            if (categoryIdsAggregation != null) {\n                result.setCategories(new ArrayList<>());\n                for (LongTerms.Bucket bucket  : (((LongTerms) categoryIdsAggregation).getBuckets())) {\n                    result.getCategories().add(new ProductConditionBO.Category().setId(bucket.getKeyAsNumber().intValue()));\n                }\n            }\n            // 返回结果\n            return result;\n        });\n        // 后续遍历 condition.categories 数组，查询商品分类，设置商品分类名。\n        System.out.println();\n    }\n\n}\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-elasticsearch/src/test/java/cn/iocoder/springboot/lab15/springdataelasticsearch/repository/ProductRepositoryTest.java",
    "content": "package cn.iocoder.springboot.lab15.springdataelasticsearch.repository;\n\nimport cn.iocoder.springboot.lab15.springdataelasticsearch.Application;\nimport cn.iocoder.springboot.lab15.springdataelasticsearch.dataobject.ESProductDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.Arrays;\nimport java.util.Optional;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class ProductRepositoryTest {\n\n    @Autowired\n    private ProductRepository productRepository;\n\n    @Test // 插入一条记录\n    public void testInsert() {\n        ESProductDO product = new ESProductDO();\n        product.setId(1); // 一般 ES 的 ID 编号，使用 DB 数据对应的编号。这里，先写死\n        product.setName(\"芋道源码\");\n        product.setSellPoint(\"愿半生编码，如一生老友\");\n        product.setDescription(\"我只是一个描述\");\n        product.setCid(2);\n        product.setCategoryName(\"技术\");\n        productRepository.save(product);\n    }\n\n    // 这里要注意，如果使用 save 方法来更新的话，必须是全量字段，否则其它字段会被覆盖。\n    // 所以，这里仅仅是作为一个示例。\n    @Test // 更新一条记录\n    public void testUpdate() {\n        ESProductDO product = new ESProductDO();\n        product.setId(1);\n        product.setCid(2);\n        product.setCategoryName(\"技术-Java\");\n        productRepository.save(product);\n    }\n\n    @Test // 根据 ID 编号，删除一条记录\n    public void testDelete() {\n        productRepository.deleteById(1);\n    }\n\n    @Test // 根据 ID 编号，查询一条记录\n    public void testSelectById() {\n        Optional<ESProductDO> userDO = productRepository.findById(1);\n        System.out.println(userDO.isPresent());\n    }\n\n    @Test // 根据 ID 编号数组，查询多条记录\n    public void testSelectByIds() {\n        Iterable<ESProductDO> users = productRepository.findAllById(Arrays.asList(1, 4));\n        users.forEach(System.out::println);\n    }\n\n}\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-jest/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-15-spring-data-jest</artifactId>\n\n    <dependencies>\n        <!-- 自动化配置 Spring Data Jest -->\n        <dependency>\n            <groupId>com.github.vanroy</groupId>\n            <artifactId>spring-boot-starter-data-jest</artifactId>\n            <version>3.2.5.RELEASE</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-jest/src/main/java/cn/iocoder/springboot/lab15/springdatajest/Application.java",
    "content": "package cn.iocoder.springboot.lab15.springdatajest;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration;\nimport org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration;\n\n@SpringBootApplication(exclude = {ElasticsearchAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class})\npublic class Application {\n}\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-jest/src/main/java/cn/iocoder/springboot/lab15/springdatajest/constant/FieldAnalyzer.java",
    "content": "package cn.iocoder.springboot.lab15.springdatajest.constant;\n\n/**\n * ES 字段分析器的枚举类\n *\n * 关于 IK 分词，文章 https://blog.csdn.net/xsdxs/article/details/72853288 不错。\n * 目前项目使用的 ES 版本是 6.7.1 ，可以在 https://www.elastic.co/cn/downloads/past-releases/elasticsearch-6-7-1 下载。\n * 如果不知道怎么安装 ES ，可以看 https://blog.csdn.net/chengyuqiang/article/details/78837712 简单。\n */\npublic class FieldAnalyzer {\n\n    /**\n     * IK 最大化分词\n     *\n     * 会将文本做最细粒度的拆分\n     */\n    public static final String IK_MAX_WORD = \"ik_max_word\";\n\n    /**\n     * IK 智能分词\n     *\n     * 会做最粗粒度的拆分\n     */\n    public static final String IK_SMART = \"ik_smart\";\n\n}\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-jest/src/main/java/cn/iocoder/springboot/lab15/springdatajest/dataobject/ESProductDO.java",
    "content": "package cn.iocoder.springboot.lab15.springdatajest.dataobject;\n\nimport cn.iocoder.springboot.lab15.springdatajest.constant.FieldAnalyzer;\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.elasticsearch.annotations.Document;\nimport org.springframework.data.elasticsearch.annotations.Field;\nimport org.springframework.data.elasticsearch.annotations.FieldType;\n\n@Document(indexName = \"product\", // 索引名\n        type = \"product\", // 类型。未来的版本即将废弃\n        shards = 1, // 默认索引分区数\n        replicas = 0, // 每个分区的备份数\n        refreshInterval = \"-1\" // 刷新间隔\n)\npublic class ESProductDO {\n\n    /**\n     * ID 主键\n     */\n    @Id\n    private Integer id;\n\n    /**\n     * SPU 名字\n     */\n    @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)\n    private String name;\n    /**\n     * 卖点\n     */\n    @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)\n    private String sellPoint;\n    /**\n     * 描述\n     */\n    @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)\n    private String description;\n    /**\n     * 分类编号\n     */\n    private Integer cid;\n    /**\n     * 分类名\n     */\n    @Field(analyzer = FieldAnalyzer.IK_MAX_WORD, type = FieldType.Text)\n    private String categoryName;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public ESProductDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public ESProductDO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public String getSellPoint() {\n        return sellPoint;\n    }\n\n    public ESProductDO setSellPoint(String sellPoint) {\n        this.sellPoint = sellPoint;\n        return this;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public ESProductDO setDescription(String description) {\n        this.description = description;\n        return this;\n    }\n\n    public Integer getCid() {\n        return cid;\n    }\n\n    public ESProductDO setCid(Integer cid) {\n        this.cid = cid;\n        return this;\n    }\n\n    public String getCategoryName() {\n        return categoryName;\n    }\n\n    public ESProductDO setCategoryName(String categoryName) {\n        this.categoryName = categoryName;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ProductDO{\" +\n                \"id=\" + id +\n                \", name='\" + name + '\\'' +\n                \", sellPoint='\" + sellPoint + '\\'' +\n                \", description='\" + description + '\\'' +\n                \", cid=\" + cid +\n                \", categoryName='\" + categoryName + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-jest/src/main/java/cn/iocoder/springboot/lab15/springdatajest/repository/ProductRepository.java",
    "content": "package cn.iocoder.springboot.lab15.springdatajest.repository;\n\nimport cn.iocoder.springboot.lab15.springdatajest.dataobject.ESProductDO;\nimport org.springframework.data.elasticsearch.repository.ElasticsearchRepository;\n\npublic interface ProductRepository extends ElasticsearchRepository<ESProductDO, Integer> {\n\n}\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-jest/src/main/java/cn/iocoder/springboot/lab15/springdatajest/repository/ProductRepository02.java",
    "content": "package cn.iocoder.springboot.lab15.springdatajest.repository;\n\nimport cn.iocoder.springboot.lab15.springdatajest.dataobject.ESProductDO;\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.elasticsearch.repository.ElasticsearchRepository;\n\npublic interface ProductRepository02 extends ElasticsearchRepository<ESProductDO, Integer> {\n\n    ESProductDO findByName(String name);\n\n    Page<ESProductDO> findByNameLike(String name, Pageable pageable);\n\n}\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-jest/src/main/java/cn/iocoder/springboot/lab15/springdatajest/repository/ProductRepository03.java",
    "content": "package cn.iocoder.springboot.lab15.springdatajest.repository;\n\nimport cn.iocoder.springboot.lab15.springdatajest.dataobject.ESProductDO;\nimport org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;\nimport org.elasticsearch.index.query.QueryBuilders;\nimport org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;\nimport org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;\nimport org.elasticsearch.search.sort.SortBuilders;\nimport org.elasticsearch.search.sort.SortOrder;\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.PageRequest;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;\nimport org.springframework.data.elasticsearch.repository.ElasticsearchRepository;\nimport org.springframework.util.StringUtils;\n\nimport static org.elasticsearch.index.query.QueryBuilders.matchQuery;\n\npublic interface ProductRepository03 extends ElasticsearchRepository<ESProductDO, Integer> {\n\n    default Page<ESProductDO> search(Integer cid, String keyword, Pageable pageable) {\n        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();\n        // 筛选条件 cid\n        if (cid != null) {\n            nativeSearchQueryBuilder.withFilter(QueryBuilders.termQuery(\"cid\", cid));\n        }\n        // 筛选\n        if (StringUtils.hasText(keyword)) {\n            FunctionScoreQueryBuilder.FilterFunctionBuilder[] functions = { // TODO 芋艿，分值随便打的\n                    new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery(\"name\", keyword),\n                            ScoreFunctionBuilders.weightFactorFunction(10)),\n                    new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery(\"sellPoint\", keyword),\n                            ScoreFunctionBuilders.weightFactorFunction(2)),\n                    new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery(\"categoryName\", keyword),\n                            ScoreFunctionBuilders.weightFactorFunction(3)),\n//                    new FunctionScoreQueryBuilder.FilterFunctionBuilder(matchQuery(\"description\", keyword),\n//                            ScoreFunctionBuilders.weightFactorFunction(2)), // TODO 芋艿，目前这么做，如果商品描述很长，在按照价格降序，会命中超级多的关键字。\n            };\n            FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(functions)\n                    .scoreMode(FunctionScoreQuery.ScoreMode.SUM)\n                    .setMinScore(2F); // TODO 芋艿，需要考虑下 score\n            nativeSearchQueryBuilder.withQuery(functionScoreQueryBuilder);\n        }\n        // 排序\n        if (StringUtils.hasText(keyword)) { // 关键字，使用打分\n            nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC));\n        } else if (pageable.getSort().isSorted()) { // 有排序，则进行拼接\n            pageable.getSort().get().forEach(sortField -> nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(sortField.getProperty())\n                    .order(sortField.getDirection().isAscending() ? SortOrder.ASC : SortOrder.DESC)));\n        } else { // 无排序，则按照 ID 倒序\n            nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(\"id\").order(SortOrder.DESC));\n        }\n        // 分页\n        nativeSearchQueryBuilder.withPageable(PageRequest.of(pageable.getPageNumber(), pageable.getPageSize())); // 避免\n        // 执行查询\n        return search(nativeSearchQueryBuilder.build());\n    }\n\n}\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-jest/src/main/resources/application.yaml",
    "content": "spring:\n  data:\n    # Jest 配置项\n    jest:\n      uri: http://127.0.0.1:9200\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-jest/src/test/java/cn/iocoder/springboot/lab15/springdatajest/repository/ProductRepository02Test.java",
    "content": "package cn.iocoder.springboot.lab15.springdatajest.repository;\n\nimport cn.iocoder.springboot.lab15.springdatajest.Application;\nimport cn.iocoder.springboot.lab15.springdatajest.dataobject.ESProductDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.PageRequest;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class ProductRepository02Test {\n\n    @Autowired\n    private ProductRepository02 productRepository;\n\n    @Test // 根据名字获得一条记录\n    public void testFindByName() {\n        ESProductDO product = productRepository.findByName(\"芋道源码\");\n        System.out.println(product);\n    }\n\n    @Test // 使用 name 模糊查询，分页返回结果\n    public void testFindByNameLike() {\n        // 根据情况，是否要制造测试数据\n        if (true) {\n            testInsert();\n        }\n\n        // 创建排序条件\n        Sort sort = new Sort(Sort.Direction.DESC, \"id\"); // ID 倒序\n        // 创建分页条件。\n        Pageable pageable = PageRequest.of(0, 10, sort);\n        // 执行分页操作\n        Page<ESProductDO> page = productRepository.findByNameLike(\"芋道\", pageable);\n        // 打印\n        System.out.println(page.getTotalElements());\n        System.out.println(page.getTotalPages());\n    }\n\n    /**\n     * 为了给分页制造一点数据\n     */\n    private void testInsert() {\n        for (int i = 1; i <= 100; i++) {\n            ESProductDO product = new ESProductDO();\n            product.setId(i); // 一般 ES 的 ID 编号，使用 DB 数据对应的编号。这里，先写死\n            product.setName(\"芋道源码：\" + i);\n            product.setSellPoint(\"愿半生编码，如一生老友\");\n            product.setDescription(\"我只是一个描述\");\n            product.setCid(1);\n            product.setCategoryName(\"技术\");\n            productRepository.save(product);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-jest/src/test/java/cn/iocoder/springboot/lab15/springdatajest/repository/ProductRepository03Test.java",
    "content": "package cn.iocoder.springboot.lab15.springdatajest.repository;\n\nimport cn.iocoder.springboot.lab15.springdatajest.Application;\nimport cn.iocoder.springboot.lab15.springdatajest.dataobject.ESProductDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.PageRequest;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class ProductRepository03Test {\n\n    @Autowired\n    private ProductRepository03 productRepository;\n\n    @Test\n    public void testSearch() {\n        // 查找分类为 1 + 指定关键字，并且按照 id 升序\n        Page<ESProductDO> page = productRepository.search(1, \"技术\",\n                PageRequest.of(0, 5, Sort.Direction.ASC, \"id\"));\n        System.out.println(page.getTotalPages());\n\n        // 查找分类为 1 ，并且按照 id 升序\n        page = productRepository.search(1, null,\n                PageRequest.of(0, 5, Sort.Direction.ASC, \"id\"));\n        System.out.println(page.getTotalPages());\n    }\n\n}\n"
  },
  {
    "path": "lab-15-spring-data-es/lab-15-spring-data-jest/src/test/java/cn/iocoder/springboot/lab15/springdatajest/repository/ProductRepositoryTest.java",
    "content": "package cn.iocoder.springboot.lab15.springdatajest.repository;\n\nimport cn.iocoder.springboot.lab15.springdatajest.Application;\nimport cn.iocoder.springboot.lab15.springdatajest.dataobject.ESProductDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.Arrays;\nimport java.util.Optional;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class ProductRepositoryTest {\n\n    @Autowired\n    private ProductRepository productRepository;\n\n    @Test // 插入一条记录\n    public void testInsert() {\n        ESProductDO product = new ESProductDO();\n        product.setId(1); // 一般 ES 的 ID 编号，使用 DB 数据对应的编号。这里，先写死\n        product.setName(\"芋道源码\");\n        product.setSellPoint(\"愿半生编码，如一生老友\");\n        product.setDescription(\"我只是一个描述\");\n        product.setCid(1);\n        product.setCategoryName(\"技术\");\n        productRepository.save(product);\n    }\n\n    // 这里要注意，如果使用 save 方法来更新的话，必须是全量字段，否则其它字段会被覆盖。\n    // 所以，这里仅仅是作为一个示例。\n    @Test // 更新一条记录\n    public void testUpdate() {\n        ESProductDO product = new ESProductDO();\n        product.setId(1);\n        product.setCid(2);\n        product.setCategoryName(\"技术-Java\");\n        productRepository.save(product);\n    }\n\n    @Test // 根据 ID 编号，删除一条记录\n    public void testDelete() {\n        productRepository.deleteById(1);\n    }\n\n    @Test // 根据 ID 编号，查询一条记录\n    public void testSelectById() {\n        Optional<ESProductDO> userDO = productRepository.findById(1);\n        System.out.println(userDO.isPresent());\n    }\n\n    @Test // 根据 ID 编号数组，查询多条记录\n    public void testSelectByIds() {\n        Iterable<ESProductDO> users = productRepository.findAllById(Arrays.asList(1, 4));\n        users.forEach(System.out::println);\n    }\n\n}\n"
  },
  {
    "path": "lab-15-spring-data-es/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-15-spring-data-es</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-15-spring-data-jest</module>\n        <module>lab-15-spring-data-elasticsearch</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-15-spring-data-es/《芋道 Spring Boot Elasticsearch 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Elasticsearch/?github>\n"
  },
  {
    "path": "lab-16-spring-data-mongo/lab-16-spring-data-mongodb/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-16-spring-data-mongodb</artifactId>\n\n    <dependencies>\n        <!-- 自动化配置 Spring Data Mongodb -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-mongodb</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/Application.java",
    "content": "package cn.iocoder.springboot.lab16.springdatamongodb;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n}\n"
  },
  {
    "path": "lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/config/MongoDBConfig.java",
    "content": "package cn.iocoder.springboot.lab16.springdatamongodb.config;\n\nimport org.springframework.beans.factory.BeanFactory;\nimport org.springframework.beans.factory.NoSuchBeanDefinitionException;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.convert.CustomConversions;\nimport org.springframework.data.mongodb.MongoDbFactory;\nimport org.springframework.data.mongodb.core.convert.DbRefResolver;\nimport org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;\nimport org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;\nimport org.springframework.data.mongodb.core.convert.MappingMongoConverter;\nimport org.springframework.data.mongodb.core.mapping.MongoMappingContext;\n\n@Configuration\npublic class MongoDBConfig {\n\n    @Bean // 目的，就是为了移除 _class field 。参考博客 https://blog.csdn.net/bigtree_3721/article/details/82787411\n    public MappingMongoConverter mappingMongoConverter(MongoDbFactory factory,\n                                                       MongoMappingContext context,\n                                                       BeanFactory beanFactory) {\n        // 创建 DbRefResolver 对象\n        DbRefResolver dbRefResolver = new DefaultDbRefResolver(factory);\n        // 创建 MappingMongoConverter 对象\n        MappingMongoConverter mappingConverter = new MappingMongoConverter(dbRefResolver, context);\n        // 设置 conversions 属性\n        try {\n            mappingConverter.setCustomConversions(beanFactory.getBean(CustomConversions.class));\n        } catch (NoSuchBeanDefinitionException ignore) {\n        }\n        // 设置 typeMapper 属性，从而移除 _class field 。\n        mappingConverter.setTypeMapper(new DefaultMongoTypeMapper(null));\n        return mappingConverter;\n    }\n\n}\n"
  },
  {
    "path": "lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/dao/UserDao.java",
    "content": "package cn.iocoder.springboot.lab16.springdatamongodb.dao;\n\nimport cn.iocoder.springboot.lab16.springdatamongodb.dataobject.UserDO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.annotation.Transient;\nimport org.springframework.data.mongodb.core.MongoTemplate;\nimport org.springframework.data.mongodb.core.query.Criteria;\nimport org.springframework.data.mongodb.core.query.Query;\nimport org.springframework.data.mongodb.core.query.Update;\nimport org.springframework.stereotype.Repository;\nimport org.springframework.util.ReflectionUtils;\n\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Modifier;\nimport java.util.List;\n\n@Repository\npublic class UserDao {\n\n    @Autowired\n    private MongoTemplate mongoTemplate;\n\n    public void insert(UserDO entity) {\n        mongoTemplate.insert(entity);\n    }\n\n    public void updateById(UserDO entity) {\n        // 生成 Update 条件\n        final Update update = new Update();\n        // 反射遍历 entity 对象，将非空字段设置到 Update 中\n        ReflectionUtils.doWithFields(entity.getClass(), new ReflectionUtils.FieldCallback() {\n\n            @Override\n            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {\n                // 排除指定条件\n                if (\"id\".equals(field.getName()) // 排除 id 字段，因为作为查询主键\n                        || field.getAnnotation(Transient.class) != null // 排除 @Transient 注解的字段，因为非存储字段\n                        || Modifier.isStatic(field.getModifiers())) { // 排除静态字段\n                    return;\n                }\n                // 设置字段可反射\n                if (!field.isAccessible()) {\n                    field.setAccessible(true);\n                }\n                // 排除字段为空的情况\n                if (field.get(entity) == null) {\n                    return;\n                }\n                // 设置更新条件\n                update.set(field.getName(), field.get(entity));\n            }\n\n        });\n        // 防御，避免有业务传递空的 Update 对象\n        if (update.getUpdateObject().isEmpty()) {\n            return;\n        }\n        // 执行更新\n        mongoTemplate.updateFirst(new Query(Criteria.where(\"_id\").is(entity.getId())), update, UserDO.class);\n    }\n\n    public void deleteById(Integer id) {\n        mongoTemplate.remove(new Query(Criteria.where(\"_id\").is(id)), UserDO.class);\n    }\n\n    public UserDO findById(Integer id) {\n        return mongoTemplate.findOne(new Query(Criteria.where(\"_id\").is(id)), UserDO.class);\n    }\n\n    public UserDO findByUsername(String username) {\n        return mongoTemplate.findOne(new Query(Criteria.where(\"username\").is(username)), UserDO.class);\n    }\n\n    public List<UserDO> findAllById(List<Integer> ids) {\n        return mongoTemplate.find(new Query(Criteria.where(\"_id\").in(ids)), UserDO.class);\n    }\n\n}\n"
  },
  {
    "path": "lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/dataobject/ProductDO.java",
    "content": "package cn.iocoder.springboot.lab16.springdatamongodb.dataobject;\n\nimport cn.iocoder.springboot.lab16.springdatamongodb.mongo.IncIdEntity;\nimport org.springframework.data.mongodb.core.mapping.Document;\n\n/**\n * 商品 DO\n */\n@Document(collection = \"Product\")\npublic class ProductDO extends IncIdEntity<Integer> {\n\n    /**\n     * 商品名\n     */\n    private String name;\n\n    public String getName() {\n        return name;\n    }\n\n    public ProductDO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab16.springdatamongodb.dataobject;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.mongodb.core.mapping.Document;\n\nimport java.util.Date;\n\n/**\n * 用户 DO\n */\n@Document(collection = \"User\")\npublic class UserDO {\n\n    /**\n     * 用户信息\n     */\n    public static class Profile {\n\n        /**\n         * 昵称\n         */\n        private String nickname;\n        /**\n         * 性别\n         */\n        private Integer gender;\n\n        public String getNickname() {\n            return nickname;\n        }\n\n        public Profile setNickname(String nickname) {\n            this.nickname = nickname;\n            return this;\n        }\n\n        public Integer getGender() {\n            return gender;\n        }\n\n        public Profile setGender(Integer gender) {\n            this.gender = gender;\n            return this;\n        }\n\n        @Override\n        public String toString() {\n            return \"Profile{\" +\n                    \"nickname='\" + nickname + '\\'' +\n                    \", gender=\" + gender +\n                    '}';\n        }\n    }\n\n    @Id\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n    /**\n     * 用户信息\n     */\n    private Profile profile;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    public UserDO setCreateTime(Date createTime) {\n        this.createTime = createTime;\n        return this;\n    }\n\n    public Profile getProfile() {\n        return profile;\n    }\n\n    public UserDO setProfile(Profile profile) {\n        this.profile = profile;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                \", createTime=\" + createTime +\n                \", profile=\" + profile +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/mongo/IncIdEntity.java",
    "content": "package cn.iocoder.springboot.lab16.springdatamongodb.mongo;\n\nimport org.springframework.data.annotation.Id;\n\n/**\n * 自增主键实体\n *\n * @param <T> 主键泛型\n */\npublic abstract class IncIdEntity<T extends Number> {\n\n    @Id\n    private T id;\n\n    public T getId() {\n        return id;\n    }\n\n    public void setId(T id) {\n        this.id = id;\n    }\n\n}\n"
  },
  {
    "path": "lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/mongo/MongoInsertEventListener.java",
    "content": "package cn.iocoder.springboot.lab16.springdatamongodb.mongo;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.mongodb.core.FindAndModifyOptions;\nimport org.springframework.data.mongodb.core.MongoTemplate;\nimport org.springframework.data.mongodb.core.mapping.event.AbstractMongoEventListener;\nimport org.springframework.data.mongodb.core.mapping.event.BeforeConvertEvent;\nimport org.springframework.data.mongodb.core.query.Criteria;\nimport org.springframework.data.mongodb.core.query.Query;\nimport org.springframework.data.mongodb.core.query.Update;\nimport org.springframework.stereotype.Component;\n\nimport java.util.HashMap;\n\n@Component\npublic class MongoInsertEventListener extends AbstractMongoEventListener<IncIdEntity> {\n\n    /**\n     * sequence - 集合名\n     */\n    private static final String SEQUENCE_COLLECTION_NAME = \"sequence\";\n    /**\n     * sequence - 自增值的字段名\n     */\n    private static final String SEQUENCE_FIELD_VALUE = \"value\";\n\n    @Autowired\n    private MongoTemplate mongoTemplate;\n\n    @Override\n    public void onBeforeConvert(BeforeConvertEvent<IncIdEntity> event) {\n        IncIdEntity entity = event.getSource();\n        // 判断 id 为空\n        if (entity.getId() == null) {\n            // 获得下一个编号\n            Number id = this.getNextId(entity);\n            // 设置到实体中\n            // noinspection unchecked\n            entity.setId(id);\n        }\n    }\n\n    /**\n     * 获得实体的下一个主键 ID 编号\n     *\n     * @param entity 实体对象\n     * @return ID 编号\n     */\n    private Number getNextId(IncIdEntity entity) {\n        // 使用实体名的简单类名，作为 ID 编号\n        String id = entity.getClass().getSimpleName();\n        // 创建 Query 对象\n        Query query = new Query(Criteria.where(\"_id\").is(id));\n        // 创建 Update 对象\n        Update update = new Update();\n        update.inc(SEQUENCE_FIELD_VALUE, 1); // 自增值\n        // 创建 FindAndModifyOptions 对象\n        FindAndModifyOptions options = new FindAndModifyOptions();\n        options.upsert(true); // 如果不存在时，则进行插入\n        options.returnNew(true); // 返回新值\n        // 执行操作\n        @SuppressWarnings(\"unchecked\")\n        HashMap<String, Object> result = mongoTemplate.findAndModify(query, update, options,\n                HashMap.class, SEQUENCE_COLLECTION_NAME);\n        // 返回主键\n        return (Number) result.get(SEQUENCE_FIELD_VALUE);\n    }\n\n}\n"
  },
  {
    "path": "lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/repository/ProductRepository.java",
    "content": "package cn.iocoder.springboot.lab16.springdatamongodb.repository;\n\nimport cn.iocoder.springboot.lab16.springdatamongodb.dataobject.ProductDO;\nimport org.springframework.data.mongodb.repository.MongoRepository;\n\npublic interface ProductRepository extends MongoRepository<ProductDO, Integer> {\n}\n"
  },
  {
    "path": "lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/repository/UserRepository.java",
    "content": "package cn.iocoder.springboot.lab16.springdatamongodb.repository;\n\nimport cn.iocoder.springboot.lab16.springdatamongodb.dataobject.UserDO;\nimport org.springframework.data.mongodb.repository.MongoRepository;\n\npublic interface UserRepository extends MongoRepository<UserDO, Integer> {\n}\n"
  },
  {
    "path": "lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/repository/UserRepository02.java",
    "content": "package cn.iocoder.springboot.lab16.springdatamongodb.repository;\n\nimport cn.iocoder.springboot.lab16.springdatamongodb.dataobject.UserDO;\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.mongodb.repository.MongoRepository;\n\npublic interface UserRepository02 extends MongoRepository<UserDO, Integer> {\n\n    UserDO findByUsername(String username);\n\n    Page<UserDO> findByUsernameLike(String username, Pageable pageable);\n\n}\n"
  },
  {
    "path": "lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/java/cn/iocoder/springboot/lab16/springdatamongodb/repository/UserRepository03.java",
    "content": "package cn.iocoder.springboot.lab16.springdatamongodb.repository;\n\nimport cn.iocoder.springboot.lab16.springdatamongodb.dataobject.UserDO;\nimport org.springframework.data.domain.Example;\nimport org.springframework.data.domain.ExampleMatcher;\nimport org.springframework.data.mongodb.repository.MongoRepository;\n\npublic interface UserRepository03 extends MongoRepository<UserDO, Integer> {\n\n    // 使用 username 精准匹配\n    default UserDO findByUsername01(String username) {\n        // 创建 Example 对象，使用 username 查询\n        UserDO probe = new UserDO();\n        probe.setUsername(username); // 精准匹配 username 查询\n        Example<UserDO> example = Example.of(probe);\n        // 执行查询\n        return findOne(example)\n                .orElse(null); // 如果为空，则返回 null\n    }\n\n    // 使用 username 模糊匹配\n    default UserDO findByUsernameLike01(String username) {\n        // 创建 Example 对象，使用 username 查询\n        UserDO probe = new UserDO();\n        probe.setUsername(username); // 这里还需要设置\n        ExampleMatcher matcher = ExampleMatcher.matching()\n                .withMatcher(\"username\", ExampleMatcher.GenericPropertyMatchers.contains()); // 模糊匹配 username 查询\n        Example<UserDO> example = Example.of(probe, matcher);\n        // 执行查询\n        return findOne(example)\n                .orElse(null); // 如果为空，则返回 null\n    }\n\n}\n"
  },
  {
    "path": "lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/main/resources/application.yaml",
    "content": "spring:\n  data:\n    # MongoDB 配置项，对应 MongoProperties 类\n    mongodb:\n      host: 127.0.0.1\n      port: 27017\n      database: yourdatabase\n      username: test01\n      password: password01\n      # 上述属性，也可以只配置 uri\n\nlogging:\n  level:\n    org:\n      springframework:\n        data:\n          mongodb:\n            core: DEBUG # 打印 mongodb 操作的具体语句。生产环境下，不建议开启。\n"
  },
  {
    "path": "lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/test/java/cn/iocoder/springboot/lab16/springdatamongodb/dao/UserDaoTest.java",
    "content": "package cn.iocoder.springboot.lab16.springdatamongodb.dao;\n\nimport cn.iocoder.springboot.lab16.springdatamongodb.Application;\nimport cn.iocoder.springboot.lab16.springdatamongodb.dataobject.UserDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.List;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserDaoTest {\n\n    @Autowired\n    private UserDao userDao;\n\n    @Test // 插入一条记录\n    public void testInsert() {\n        // 创建 UserDO 对象\n        UserDO user = new UserDO();\n        user.setId(1); // 这里先临时写死一个 ID 编号，后面演示自增 ID 的时候，在修改这块\n        user.setUsername(\"yudaoyuanma\");\n        user.setPassword(\"buzhidao\");\n        user.setCreateTime(new Date());\n        // 创建 Profile 对象\n        UserDO.Profile profile = new UserDO.Profile();\n        profile.setNickname(\"芋道源码\");\n        profile.setGender(1);\n        user.setProfile(profile);\n        // 存储到 DB\n        userDao.insert(user);\n    }\n\n    // 这里要注意，如果使用 save 方法来更新的话，必须是全量字段，否则其它字段会被覆盖。\n    // 所以，这里仅仅是作为一个示例。\n    @Test // 更新一条记录\n    public void testUpdate() {\n        // 创建 UserDO 对象\n        UserDO updateUser = new UserDO();\n        updateUser.setId(1);\n        updateUser.setUsername(\"nicai\");\n\n        // 执行更新\n        userDao.updateById(updateUser);\n    }\n\n    @Test // 根据 ID 编号，删除一条记录\n    public void testDelete() {\n        userDao.deleteById(1);\n    }\n\n    @Test // 根据 ID 编号，查询一条记录\n    public void testSelectById() {\n        UserDO userDO = userDao.findById(1);\n        System.out.println(userDO);\n    }\n\n    @Test // 根据 ID 编号数组，查询多条记录\n    public void testSelectByIds() {\n        List<UserDO> users = userDao.findAllById(Arrays.asList(1, 4));\n        users.forEach(System.out::println);\n    }\n\n}\n"
  },
  {
    "path": "lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/test/java/cn/iocoder/springboot/lab16/springdatamongodb/repository/ProductRepositoryTest.java",
    "content": "package cn.iocoder.springboot.lab16.springdatamongodb.repository;\n\nimport cn.iocoder.springboot.lab16.springdatamongodb.Application;\nimport cn.iocoder.springboot.lab16.springdatamongodb.dataobject.ProductDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class ProductRepositoryTest {\n\n    @Autowired\n    private ProductRepository productRepository;\n\n    @Test\n    public void testInsert() {\n        // 创建 ProductDO 对象\n        ProductDO product = new ProductDO();\n        product.setName(\"芋头\");\n        // 插入\n        productRepository.insert(product);\n        // 打印 ID\n        System.out.println(product.getId());\n    }\n\n}\n"
  },
  {
    "path": "lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/test/java/cn/iocoder/springboot/lab16/springdatamongodb/repository/UserRepository02Test.java",
    "content": "package cn.iocoder.springboot.lab16.springdatamongodb.repository;\n\nimport cn.iocoder.springboot.lab16.springdatamongodb.Application;\nimport cn.iocoder.springboot.lab16.springdatamongodb.dataobject.UserDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.PageRequest;\nimport org.springframework.data.domain.Pageable;\nimport org.springframework.data.domain.Sort;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserRepository02Test {\n\n    @Autowired\n    private UserRepository02 userRepository;\n\n    @Test // 根据名字获得一条记录\n    public void testFindByName() {\n        UserDO user = userRepository.findByUsername(\"yutou\");\n        System.out.println(user);\n    }\n\n    @Test // 使用 username 模糊查询，分页返回结果\n    public void testFindByNameLike() {\n        // 创建排序条件\n        Sort sort = new Sort(Sort.Direction.DESC, \"id\"); // ID 倒序\n        // 创建分页条件。\n        Pageable pageable = PageRequest.of(0, 10, sort);\n        // 执行分页操作\n        Page<UserDO> page = userRepository.findByUsernameLike(\"yu\", pageable);\n        // 打印\n        System.out.println(page.getTotalElements());\n        System.out.println(page.getTotalPages());\n    }\n\n}\n"
  },
  {
    "path": "lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/test/java/cn/iocoder/springboot/lab16/springdatamongodb/repository/UserRepository03Test.java",
    "content": "package cn.iocoder.springboot.lab16.springdatamongodb.repository;\n\nimport cn.iocoder.springboot.lab16.springdatamongodb.Application;\nimport cn.iocoder.springboot.lab16.springdatamongodb.dataobject.UserDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserRepository03Test {\n\n    @Autowired\n    private UserRepository03 userRepository;\n\n    @Test\n    public void testFindByUsername01() {\n        UserDO user = userRepository.findByUsername01(\"yutou\");\n        System.out.println(user);\n    }\n\n    @Test\n    public void testFindByUsernameLike01() {\n        UserDO user = userRepository.findByUsernameLike01(\"yu\");\n        System.out.println(user);\n    }\n\n}\n"
  },
  {
    "path": "lab-16-spring-data-mongo/lab-16-spring-data-mongodb/src/test/java/cn/iocoder/springboot/lab16/springdatamongodb/repository/UserRepositoryTest.java",
    "content": "package cn.iocoder.springboot.lab16.springdatamongodb.repository;\n\nimport cn.iocoder.springboot.lab16.springdatamongodb.Application;\nimport cn.iocoder.springboot.lab16.springdatamongodb.dataobject.UserDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.util.Assert;\n\nimport java.util.Arrays;\nimport java.util.Date;\nimport java.util.Optional;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserRepositoryTest {\n\n    @Autowired\n    private UserRepository userRepository;\n\n    @Test // 插入一条记录\n    public void testInsert() {\n        // 创建 UserDO 对象\n        UserDO user = new UserDO();\n        user.setId(1); // 这里先临时写死一个 ID 编号，后面演示自增 ID 的时候，在修改这块\n        user.setUsername(\"yudaoyuanma\");\n        user.setPassword(\"buzhidao\");\n        user.setCreateTime(new Date());\n        // 创建 Profile 对象\n        UserDO.Profile profile = new UserDO.Profile();\n        profile.setNickname(\"芋道源码\");\n        profile.setGender(1);\n        user.setProfile(profile);\n        // 存储到 DB\n        userRepository.insert(user);\n    }\n\n    // 这里要注意，如果使用 save 方法来更新的话，必须是全量字段，否则其它字段会被覆盖。\n    // 所以，这里仅仅是作为一个示例。\n    @Test // 更新一条记录\n    public void testUpdate() {\n        // 查询用户\n        Optional<UserDO> userResult = userRepository.findById(1);\n        Assert.isTrue(userResult.isPresent(), \"用户一定要存在\");\n        // 更新\n        UserDO updateUser = userResult.get();\n        updateUser.setUsername(\"yutou\");\n        userRepository.save(updateUser);\n    }\n\n    @Test // 根据 ID 编号，删除一条记录\n    public void testDelete() {\n        userRepository.deleteById(1);\n    }\n\n    @Test // 根据 ID 编号，查询一条记录\n    public void testSelectById() {\n        Optional<UserDO> userDO = userRepository.findById(1);\n        System.out.println(userDO.isPresent());\n    }\n\n    @Test // 根据 ID 编号数组，查询多条记录\n    public void testSelectByIds() {\n        Iterable<UserDO> users = userRepository.findAllById(Arrays.asList(1, 4));\n        users.forEach(System.out::println);\n    }\n\n}\n"
  },
  {
    "path": "lab-16-spring-data-mongo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-16-spring-data-mongo</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-16-spring-data-mongodb</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-16-spring-data-mongo/《芋道 Spring Boot MongoDB 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/MongoDB/?github>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-17-dynamic-datasource-baomidou-01</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.1</version>\n        </dependency>\n\n        <!-- 实现对 dynamic-datasource 的自动化配置 -->\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>\n            <version>2.5.7</version>\n        </dependency>\n        <!-- 不造为啥 dynamic-datasource-spring-boot-starter 会依赖这个 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-actuator</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/Application.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource;\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.EnableAspectJAutoProxy;\n\n@SpringBootApplication\n@MapperScan(basePackages = \"cn.iocoder.springboot.lab17.dynamicdatasource.mapper\")\n@EnableAspectJAutoProxy(exposeProxy = true) // http://www.voidcn.com/article/p-zddcuyii-bpt.html\npublic class Application {\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/constant/DBConstants.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.constant;\n\n/**\n * 数据库枚举类\n */\npublic class DBConstants {\n\n    /**\n     * 数据源分组 - 订单库\n     */\n    public static final String DATASOURCE_ORDERS = \"orders\";\n\n    /**\n     * 数据源分组 - 用户库\n     */\n    public static final String DATASOURCE_USERS = \"users\";\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/OrderDO.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject;\n\n/**\n * 订单 DO\n */\npublic class OrderDO {\n\n    /**\n     * 订单编号\n     */\n    private Integer id;\n    /**\n     * 用户编号\n     */\n    private Integer userId;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public OrderDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getUserId() {\n        return userId;\n    }\n\n    public OrderDO setUserId(Integer userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"OrderDO{\" +\n                \"id=\" + id +\n                \", userId=\" + userId +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject;\n\n/**\n * 用户 DO\n */\npublic class UserDO {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/OrderMapper.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.mapper;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\n\n@Repository\n@DS(DBConstants.DATASOURCE_ORDERS)\npublic interface OrderMapper {\n\n    OrderDO selectById(@Param(\"id\") Integer id);\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/UserMapper.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.mapper;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\n\n@Repository\n@DS(DBConstants.DATASOURCE_USERS)\npublic interface UserMapper {\n\n    UserDO selectById(@Param(\"id\") Integer id);\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderService.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.service;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.mapper.OrderMapper;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.mapper.UserMapper;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport org.springframework.aop.framework.AopContext;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Propagation;\nimport org.springframework.transaction.annotation.Transactional;\n\n@Service\npublic class OrderService {\n\n    @Autowired\n    private OrderMapper orderMapper;\n    @Autowired\n    private UserMapper userMapper;\n\n    private OrderService self() {\n        return (OrderService) AopContext.currentProxy();\n    }\n\n    public void method01() {\n        // 查询订单\n        OrderDO order = orderMapper.selectById(1);\n        System.out.println(order);\n        // 查询用户\n        UserDO user = userMapper.selectById(1);\n        System.out.println(user);\n    }\n\n    @Transactional\n    public void method02() {\n        // 查询订单\n        OrderDO order = orderMapper.selectById(1);\n        System.out.println(order);\n        // 查询用户\n        UserDO user = userMapper.selectById(1);\n        System.out.println(user);\n    }\n\n    public void method03() {\n        // 查询订单\n        self().method031();\n        // 查询用户\n        self().method032();\n    }\n\n    @Transactional // 报错，因为此时获取的是 primary 对应的 DataSource ，即 users 。\n    public void method031() {\n        OrderDO order = orderMapper.selectById(1);\n        System.out.println(order);\n    }\n\n    @Transactional\n    public void method032() {\n        UserDO user = userMapper.selectById(1);\n        System.out.println(user);\n    }\n\n    public void method04() {\n        // 查询订单\n        self().method041();\n        // 查询用户\n        self().method042();\n    }\n\n    @Transactional\n    @DS(DBConstants.DATASOURCE_ORDERS)\n    public void method041() {\n        OrderDO order = orderMapper.selectById(1);\n        System.out.println(order);\n    }\n\n    @Transactional\n    @DS(DBConstants.DATASOURCE_USERS)\n    public void method042() {\n        UserDO user = userMapper.selectById(1);\n        System.out.println(user);\n    }\n\n    @Transactional\n    @DS(DBConstants.DATASOURCE_ORDERS)\n    public void method05() {\n        // 查询订单\n        OrderDO order = orderMapper.selectById(1);\n        System.out.println(order);\n        // 查询用户\n        self().method052();\n    }\n\n    @Transactional(propagation = Propagation.REQUIRES_NEW)\n    @DS(DBConstants.DATASOURCE_USERS)\n    public void method052() {\n        UserDO user = userMapper.selectById(1);\n        System.out.println(user);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/resources/application.yaml",
    "content": "spring:\n  datasource:\n    # dynamic-datasource-spring-boot-starter 动态数据源的配置内容\n    dynamic:\n      primary: users # 设置默认的数据源或者数据源组，默认值即为 master\n      datasource:\n        # 订单 orders 数据源配置\n        orders:\n          url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n          driver-class-name: com.mysql.jdbc.Driver\n          username: root\n          password:\n        # 用户 users 数据源配置\n        users:\n          url: jdbc:mysql://127.0.0.1:3306/test_users?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n          driver-class-name: com.mysql.jdbc.Driver\n          username: root\n          password:\n\n# mybatis 配置内容\nmybatis:\n  config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径\n  mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址\n  type-aliases-package: cn.iocoder.springboot.lab17.dynamicdatasource.dataobject # 配置数据库实体包路径\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/resources/mapper/OrderMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.springboot.lab17.dynamicdatasource.mapper.OrderMapper\">\n\n    <sql id=\"FIELDS\">\n        id, user_id\n    </sql>\n\n    <select id=\"selectById\" parameterType=\"Integer\" resultType=\"OrderDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM orders\n        WHERE id = #{id}\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/resources/mapper/UserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.springboot.lab17.dynamicdatasource.mapper.UserMapper\">\n\n    <sql id=\"FIELDS\">\n        id, username\n    </sql>\n\n    <select id=\"selectById\" parameterType=\"Integer\" resultType=\"UserDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM users\n        WHERE id = #{id}\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/resources/mybatis-config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE configuration PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n<configuration>\n\n    <settings>\n        <!-- 使用驼峰命名法转换字段。 -->\n        <setting name=\"mapUnderscoreToCamelCase\" value=\"true\"/>\n    </settings>\n\n    <typeAliases>\n        <typeAlias alias=\"Integer\" type=\"java.lang.Integer\"/>\n        <typeAlias alias=\"Long\" type=\"java.lang.Long\"/>\n        <typeAlias alias=\"HashMap\" type=\"java.util.HashMap\"/>\n        <typeAlias alias=\"LinkedHashMap\" type=\"java.util.LinkedHashMap\"/>\n        <typeAlias alias=\"ArrayList\" type=\"java.util.ArrayList\"/>\n        <typeAlias alias=\"LinkedList\" type=\"java.util.LinkedList\"/>\n    </typeAliases>\n\n</configuration>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-01/src/main/resources/sql/db.sql",
    "content": "CREATE TABLE `users` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',\n  `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_username` (`username`)\n) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n\n\n\nCREATE TABLE `orders` (\n  `id` int(11) DEFAULT NULL COMMENT '订单编号',\n  `user_id` int(16) DEFAULT NULL COMMENT '用户编号'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表';\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-01/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/OrderMapperTest.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.mapper;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.Application;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class OrderMapperTest {\n\n    @Autowired\n    private OrderMapper orderMapper;\n\n    @Test\n    public void testSelectById() {\n        OrderDO order = orderMapper.selectById(1);\n        System.out.println(order);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-01/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/UserMapperTest.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.mapper;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.Application;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserMapperTest {\n\n    @Autowired\n    private UserMapper userMapper;\n\n    @Test\n    public void testSelectById() {\n        UserDO user = userMapper.selectById(1);\n        System.out.println(user);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-01/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderServiceTest.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.service;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class OrderServiceTest {\n\n    @Autowired\n    private OrderService orderService;\n\n    @Test\n    public void testMethod01() {\n        orderService.method01();\n    }\n\n    @Test\n    public void testMethod02() {\n        orderService.method02();\n    }\n\n    @Test\n    public void testMethod03() {\n        orderService.method03();\n    }\n\n    @Test\n    public void testMethod04() {\n        orderService.method04();\n    }\n\n    @Test\n    public void testMethod05() {\n        orderService.method05();\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-02/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-17-dynamic-datasource-baomidou-02</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.1</version>\n        </dependency>\n\n        <!-- 实现对 dynamic-datasource 的自动化配置 -->\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>\n            <version>2.5.7</version>\n        </dependency>\n        <!-- 不造为啥 dynamic-datasource-spring-boot-starter 会依赖这个 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-actuator</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-02/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/Application.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource;\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.EnableAspectJAutoProxy;\n\n@SpringBootApplication\n@MapperScan(basePackages = \"cn.iocoder.springboot.lab17.dynamicdatasource.mapper\")\n@EnableAspectJAutoProxy(exposeProxy = true) // http://www.voidcn.com/article/p-zddcuyii-bpt.html\npublic class Application {\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-02/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/constant/DBConstants.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.constant;\n\n/**\n * 数据库枚举类\n */\npublic class DBConstants {\n\n    /**\n     * 数据源分组 - 订单库 - 主库\n     */\n    public static final String DATASOURCE_MASTER = \"master\";\n\n    /**\n     * 数据源分组 - 订单库 - 从库\n     */\n    public static final String DATASOURCE_SLAVE = \"slave\";\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-02/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/OrderDO.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject;\n\n/**\n * 订单 DO\n */\npublic class OrderDO {\n\n    /**\n     * 订单编号\n     */\n    private Integer id;\n    /**\n     * 用户编号\n     */\n    private Integer userId;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public OrderDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getUserId() {\n        return userId;\n    }\n\n    public OrderDO setUserId(Integer userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"OrderDO{\" +\n                \"id=\" + id +\n                \", userId=\" + userId +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-02/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/OrderMapper.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.mapper;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic interface OrderMapper {\n\n    @DS(DBConstants.DATASOURCE_SLAVE)\n    OrderDO selectById(@Param(\"id\") Integer id);\n\n    @DS(DBConstants.DATASOURCE_MASTER)\n    int insert(OrderDO entity);\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-02/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderService.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.service;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.mapper.OrderMapper;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\n@Service\npublic class OrderService {\n\n    @Autowired\n    private OrderMapper orderMapper;\n\n    @Transactional\n    @DS(DBConstants.DATASOURCE_MASTER)\n    public void add(OrderDO order) {\n        // 这里先假模假样的读取一下，\n        OrderDO exists = orderMapper.selectById(order.getId());\n        System.out.println(exists);\n\n        // 插入订单\n        orderMapper.insert(order);\n    }\n\n    public OrderDO findById(Integer id) {\n        return orderMapper.selectById(id);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-02/src/main/resources/application.yaml",
    "content": "spring:\n  datasource:\n    # dynamic-datasource-spring-boot-starter 动态数据源的配置内容\n    dynamic:\n      primary: master # 设置默认的数据源或者数据源组，默认值即为 master\n      datasource:\n        # 订单 orders 主库的数据源配置\n        master:\n          url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n          driver-class-name: com.mysql.jdbc.Driver\n          username: root\n          password:\n        # 订单 orders 从库数据源配置\n        slave_1:\n          url: jdbc:mysql://127.0.0.1:3306/test_orders_01?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n          driver-class-name: com.mysql.jdbc.Driver\n          username: root\n          password:\n        # 订单 orders 从库数据源配置\n        slave_2:\n          url: jdbc:mysql://127.0.0.1:3306/test_orders_02?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n          driver-class-name: com.mysql.jdbc.Driver\n          username: root\n          password:\n\n# mybatis 配置内容\nmybatis:\n  config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径\n  mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址\n  type-aliases-package: cn.iocoder.springboot.lab17.dynamicdatasource.dataobject # 配置数据库实体包路径\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-02/src/main/resources/mapper/OrderMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.springboot.lab17.dynamicdatasource.mapper.OrderMapper\">\n\n    <sql id=\"FIELDS\">\n        id, user_id\n    </sql>\n\n    <select id=\"selectById\" parameterType=\"Integer\" resultType=\"OrderDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM orders\n        WHERE id = #{id}\n    </select>\n\n    <insert id=\"insert\" parameterType=\"OrderDO\" useGeneratedKeys=\"true\" keyProperty=\"id\">\n        INSERT INTO orders (\n          user_id\n        ) VALUES (\n          #{userId}\n        )\n    </insert>\n\n</mapper>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-02/src/main/resources/mybatis-config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE configuration PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n<configuration>\n\n    <settings>\n        <!-- 使用驼峰命名法转换字段。 -->\n        <setting name=\"mapUnderscoreToCamelCase\" value=\"true\"/>\n    </settings>\n\n    <typeAliases>\n        <typeAlias alias=\"Integer\" type=\"java.lang.Integer\"/>\n        <typeAlias alias=\"Long\" type=\"java.lang.Long\"/>\n        <typeAlias alias=\"HashMap\" type=\"java.util.HashMap\"/>\n        <typeAlias alias=\"LinkedHashMap\" type=\"java.util.LinkedHashMap\"/>\n        <typeAlias alias=\"ArrayList\" type=\"java.util.ArrayList\"/>\n        <typeAlias alias=\"LinkedList\" type=\"java.util.LinkedList\"/>\n    </typeAliases>\n\n</configuration>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-02/src/main/resources/sql/db.sql",
    "content": "CREATE TABLE `users` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',\n  `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_username` (`username`)\n) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n\n\n\nCREATE TABLE `orders` (\n  `id` int(11) DEFAULT NULL COMMENT '订单编号',\n  `user_id` int(16) DEFAULT NULL COMMENT '用户编号'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表';\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-02/src/test/java/dynamicdatasource/mapper/OrderMapperTest.java",
    "content": "package dynamicdatasource.mapper;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.Application;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.mapper.OrderMapper;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class OrderMapperTest {\n\n    @Autowired\n    private OrderMapper orderMapper;\n\n    @Test\n    public void testSelectById() {\n        for (int i = 0; i < 10; i++) {\n            OrderDO order = orderMapper.selectById(1);\n            System.out.println(order);\n        }\n    }\n\n    @Test\n    public void testInsert() {\n        OrderDO order = new OrderDO();\n        order.setUserId(10);\n        orderMapper.insert(order);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-baomidou-02/src/test/java/dynamicdatasource/service/OrderServiceTest.java",
    "content": "package dynamicdatasource.service;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.Application;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.service.OrderService;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class OrderServiceTest {\n\n    @Autowired\n    private OrderService orderService;\n\n    @Test\n    public void testAdd() {\n        OrderDO order = new OrderDO();\n        order.setUserId(20);\n        orderService.add(order);\n    }\n\n    @Test\n    public void testFindById() {\n        OrderDO order = orderService.findById(1);\n        System.out.println(order);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-jdbctemplate/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-17-dynamic-datasource-jdbctemplate</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 保证 Spring AOP 相关的依赖包 -->\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-aspects</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n\n</project>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/Application.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.EnableAspectJAutoProxy;\n\n@SpringBootApplication\n@EnableAspectJAutoProxy(exposeProxy = true) // http://www.voidcn.com/article/p-zddcuyii-bpt.html\npublic class Application {\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/config/JdbcTemplateOrdersConfig.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.config;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.boot.jdbc.DataSourceBuilder;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.DataSourceTransactionManager;\nimport org.springframework.transaction.PlatformTransactionManager;\n\nimport javax.sql.DataSource;\n\n@Configuration\npublic class JdbcTemplateOrdersConfig {\n\n    /**\n     * 创建 orders 数据源\n     */\n    @Bean(name = \"ordersDataSource\")\n    @ConfigurationProperties(prefix = \"spring.datasource.orders\")\n    public DataSource dataSource() {\n        return DataSourceBuilder.create().build();\n    }\n\n    /**\n     * 创建 orders JdbcTemplate\n     */\n    @Bean(name = DBConstants.JDBC_TEMPLATE_ORDERS)\n    public JdbcTemplate jdbcTemplate() {\n        return new JdbcTemplate(this.dataSource());\n    }\n\n    /**\n     * 创建 orders 数据源的 TransactionManager 事务管理器\n     */\n    @Bean(name = DBConstants.TX_MANAGER_ORDERS)\n    public PlatformTransactionManager transactionManager() {\n        return new DataSourceTransactionManager(this.dataSource());\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/config/JdbcTemplateUsersConfig.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.config;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.boot.jdbc.DataSourceBuilder;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.datasource.DataSourceTransactionManager;\nimport org.springframework.transaction.PlatformTransactionManager;\n\nimport javax.sql.DataSource;\n\n@Configuration\npublic class JdbcTemplateUsersConfig {\n\n    /**\n     * 创建 users 数据源\n     */\n    @Bean(name = \"usersDataSource\")\n    @ConfigurationProperties(prefix = \"spring.datasource.users\")\n    public DataSource dataSource() {\n        return DataSourceBuilder.create().build();\n    }\n\n    /**\n     * 创建 users JdbcTemplate\n     */\n    @Bean(name = DBConstants.JDBC_TEMPLATE_USERS)\n    public JdbcTemplate jdbcTemplate() {\n        return new JdbcTemplate(this.dataSource());\n    }\n\n    /**\n     * 创建 users 数据源的 TransactionManager 事务管理器\n     */\n    @Bean(name = DBConstants.TX_MANAGER_USERS)\n    public PlatformTransactionManager transactionManager() {\n        return new DataSourceTransactionManager(this.dataSource());\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/constant/DBConstants.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.constant;\n\n/**\n * 数据库枚举类\n */\npublic class DBConstants {\n\n    /**\n     * 事务管理器 - 订单库\n     */\n    public static final String TX_MANAGER_ORDERS = \"ordersTransactionManager\";\n    /**\n     * 事务管理器 - 用户库\n     */\n    public static final String TX_MANAGER_USERS = \"usersTransactionManager\";\n\n    /**\n     * JdbcTemplate - 订单库\n     */\n    public static final String JDBC_TEMPLATE_ORDERS = \"ordersJdbcTemplate\";\n    /**\n     * JdbcTemplate - 用户库\n     */\n    public static final String JDBC_TEMPLATE_USERS = \"usersJdbcTemplate\";\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dao/OrderDao.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.dao;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport org.springframework.jdbc.core.BeanPropertyRowMapper;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.stereotype.Repository;\n\nimport javax.annotation.Resource;\n\n@Repository\npublic class OrderDao {\n\n    @Resource(name = DBConstants.JDBC_TEMPLATE_ORDERS)\n    private JdbcTemplate template;\n\n    public OrderDO selectById(Integer id) {\n        return template.queryForObject(\"SELECT id, user_id FROM orders WHERE id = ?\",\n                new BeanPropertyRowMapper<>(OrderDO.class), // 结果转换成对应的对象\n                id);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dao/UserDao.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.dao;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO;\nimport org.springframework.jdbc.core.BeanPropertyRowMapper;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.stereotype.Repository;\n\nimport javax.annotation.Resource;\n\n@Repository\npublic class UserDao {\n\n    @Resource(name = DBConstants.JDBC_TEMPLATE_USERS)\n    private JdbcTemplate template;\n\n    public UserDO selectById(Integer id) {\n        return template.queryForObject(\"SELECT id, username FROM users WHERE id = ?\",\n                new BeanPropertyRowMapper<>(UserDO.class), // 结果转换成对应的对象\n                id);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/OrderDO.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject;\n\n/**\n * 订单 DO\n */\npublic class OrderDO {\n\n    /**\n     * 订单编号\n     */\n    private Integer id;\n    /**\n     * 用户编号\n     */\n    private Integer userId;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public OrderDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getUserId() {\n        return userId;\n    }\n\n    public OrderDO setUserId(Integer userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"OrderDO{\" +\n                \"id=\" + id +\n                \", userId=\" + userId +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject;\n\n/**\n * 用户 DO\n */\npublic class UserDO {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderService.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.service;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dao.OrderDao;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dao.UserDao;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO;\nimport org.springframework.aop.framework.AopContext;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Propagation;\nimport org.springframework.transaction.annotation.Transactional;\n\n@Service\npublic class OrderService {\n\n    @Autowired\n    private OrderDao orderDao;\n    @Autowired\n    private UserDao userDao;\n\n    private OrderService self() {\n        return (OrderService) AopContext.currentProxy();\n    }\n\n    public void method01() {\n        // 查询订单\n        OrderDO order = orderDao.selectById(1);\n        System.out.println(order);\n        // 查询用户\n        UserDO user = userDao.selectById(1);\n        System.out.println(user);\n    }\n\n    @Transactional // 报错，找不到事务管理器\n    public void method02() {\n        // 查询订单\n        OrderDO order = orderDao.selectById(1);\n        System.out.println(order);\n        // 查询用户\n        UserDO user = userDao.selectById(1);\n        System.out.println(user);\n    }\n\n    public void method03() {\n        // 查询订单\n        self().method031();\n        // 查询用户\n        self().method032();\n    }\n\n    @Transactional(transactionManager = DBConstants.TX_MANAGER_ORDERS)\n    public void method031() {\n        OrderDO order = orderDao.selectById(1);\n        System.out.println(order);\n    }\n\n    @Transactional(transactionManager = DBConstants.TX_MANAGER_USERS)\n    public void method032() {\n        UserDO user = userDao.selectById(1);\n        System.out.println(user);\n    }\n\n    @Transactional(transactionManager = DBConstants.TX_MANAGER_ORDERS)\n    public void method05() {\n        // 查询订单\n        OrderDO order = orderDao.selectById(1);\n        System.out.println(order);\n        // 查询用户\n        self().method052();\n    }\n\n    @Transactional(transactionManager = DBConstants.TX_MANAGER_USERS,\n            propagation = Propagation.REQUIRES_NEW)\n    public void method052() {\n        UserDO user = userDao.selectById(1);\n        System.out.println(user);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-jdbctemplate/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    # 订单数据源配置\n    orders:\n      jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n      driver-class-name: com.mysql.jdbc.Driver\n      username: root\n      password:\n    # 用户数据源配置\n    users:\n      jdbc-url: jdbc:mysql://127.0.0.1:3306/test_users?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n      driver-class-name: com.mysql.jdbc.Driver\n      username: root\n      password:\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-jdbctemplate/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/dao/OrderDaoTest.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.dao;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.Application;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class OrderDaoTest {\n\n    @Autowired\n    private OrderDao orderDao;\n\n    @Test\n    public void testSelectById() {\n        OrderDO order = orderDao.selectById(1);\n        System.out.println(order);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-jdbctemplate/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/dao/UserDaoTest.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.dao;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.Application;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserDaoTest {\n\n    @Autowired\n    private UserDao userDao;\n\n    @Test\n    public void testSelectById() {\n        UserDO user = userDao.selectById(1);\n        System.out.println(user);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-jdbctemplate/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/package-info.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource;\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-jdbctemplate/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderServiceTest.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.service;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class OrderServiceTest {\n\n    @Autowired\n    private OrderService orderService;\n\n    @Test\n    public void testMethod01() {\n        orderService.method01();\n    }\n\n    @Test\n    public void testMethod02() {\n        orderService.method02();\n    }\n\n    @Test\n    public void testMethod03() {\n        orderService.method03();\n    }\n\n    @Test\n    public void testMethod05() {\n        orderService.method05();\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-17-dynamic-datasource-mybatis</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- MyBatis 相关依赖 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.1</version>\n        </dependency>\n\n        <!-- 保证 Spring AOP 相关的依赖包 -->\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-aspects</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/Application.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.EnableAspectJAutoProxy;\n\n@SpringBootApplication\n@EnableAspectJAutoProxy(exposeProxy = true) // http://www.voidcn.com/article/p-zddcuyii-bpt.html\npublic class Application {\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/config/MyBatisOrdersConfig.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.config;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.mybatis.spring.SqlSessionFactoryBean;\nimport org.mybatis.spring.SqlSessionTemplate;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.boot.jdbc.DataSourceBuilder;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.io.support.PathMatchingResourcePatternResolver;\nimport org.springframework.jdbc.datasource.DataSourceTransactionManager;\nimport org.springframework.transaction.PlatformTransactionManager;\n\nimport javax.sql.DataSource;\n\n@Configuration\n@MapperScan(basePackages = \"cn.iocoder.springboot.lab17.dynamicdatasource.mapper.orders\", sqlSessionTemplateRef = \"ordersSqlSessionTemplate\")\npublic class MyBatisOrdersConfig {\n\n    /**\n     * 创建 orders 数据源\n     */\n    @Bean(name = \"ordersDataSource\")\n    @ConfigurationProperties(prefix = \"spring.datasource.orders\")\n    public DataSource dataSource() {\n        return DataSourceBuilder.create().build();\n    }\n\n    /**\n     * 创建 MyBatis SqlSessionFactory\n     */\n    @Bean(name = \"ordersSqlSessionFactory\")\n    public SqlSessionFactory sqlSessionFactory() throws Exception {\n        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();\n        // 设置 orders 数据源\n        bean.setDataSource(this.dataSource());\n        // 设置 entity 所在包\n        bean.setTypeAliasesPackage(\"cn.iocoder.springboot.lab17.dynamicdatasource.dataobject\");\n        // 设置 config 路径\n        bean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource(\"classpath:mybatis-config.xml\"));\n        // 设置 mapper 路径\n        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(\"classpath:mapper/orders/*.xml\"));\n        return bean.getObject();\n    }\n\n    /**\n     * 创建 MyBatis SqlSessionTemplate\n     */\n    @Bean(name = \"ordersSqlSessionTemplate\")\n    public SqlSessionTemplate sqlSessionTemplate() throws Exception {\n        return new SqlSessionTemplate(this.sqlSessionFactory());\n    }\n\n    /**\n     * 创建 orders 数据源的 TransactionManager 事务管理器\n     */\n    @Bean(name = DBConstants.TX_MANAGER_ORDERS)\n    public PlatformTransactionManager transactionManager() {\n        return new DataSourceTransactionManager(this.dataSource());\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/config/MyBatisUsersConfig.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.config;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants;\nimport org.apache.ibatis.session.SqlSessionFactory;\nimport org.mybatis.spring.SqlSessionFactoryBean;\nimport org.mybatis.spring.SqlSessionTemplate;\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.boot.jdbc.DataSourceBuilder;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.io.support.PathMatchingResourcePatternResolver;\nimport org.springframework.jdbc.datasource.DataSourceTransactionManager;\nimport org.springframework.transaction.PlatformTransactionManager;\n\nimport javax.sql.DataSource;\n\n@Configuration\n@MapperScan(basePackages = \"cn.iocoder.springboot.lab17.dynamicdatasource.mapper.users\", sqlSessionTemplateRef = \"usersSqlSessionTemplate\")\npublic class MyBatisUsersConfig {\n\n    /**\n     * 创建 users 数据源\n     */\n    @Bean(name = \"usersDataSource\")\n    @ConfigurationProperties(prefix = \"spring.datasource.users\")\n    public DataSource dataSource() {\n        return DataSourceBuilder.create().build();\n    }\n\n    /**\n     * 创建 MyBatis SqlSessionFactory\n     */\n    @Bean(name = \"sqlSessionFactory\")\n    public SqlSessionFactory sqlSessionFactory() throws Exception {\n        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();\n        // 设置 users 数据源\n        bean.setDataSource(this.dataSource());\n        // 设置 entity 所在包\n        bean.setTypeAliasesPackage(\"cn.iocoder.springboot.lab17.dynamicdatasource.dataobject\");\n        // 设置 config 路径\n        bean.setConfigLocation(new PathMatchingResourcePatternResolver().getResource(\"classpath:mybatis-config.xml\"));\n        // 设置 mapper 路径\n        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(\"classpath:mapper/users/*.xml\"));\n        return bean.getObject();\n    }\n\n    /**\n     * 创建 MyBatis SqlSessionTemplate\n     */\n    @Bean(name = \"usersSqlSessionTemplate\")\n    public SqlSessionTemplate sqlSessionTemplate() throws Exception {\n        return new SqlSessionTemplate(this.sqlSessionFactory());\n    }\n\n    /**\n     * 创建 users 数据源的 TransactionManager 事务管理器\n     */\n    @Bean(name = DBConstants.TX_MANAGER_USERS)\n    public PlatformTransactionManager transactionManager() {\n        return new DataSourceTransactionManager(this.dataSource());\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/constant/DBConstants.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.constant;\n\n/**\n * 数据库枚举类\n */\npublic class DBConstants {\n\n    /**\n     * 事务管理器 - 订单库\n     */\n    public static final String TX_MANAGER_ORDERS = \"ordersTransactionManager\";\n\n    /**\n     * 事务管理器 - 用户库\n     */\n    public static final String TX_MANAGER_USERS = \"usersTransactionManager\";\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/OrderDO.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject;\n\n/**\n * 订单 DO\n */\npublic class OrderDO {\n\n    /**\n     * 订单编号\n     */\n    private Integer id;\n    /**\n     * 用户编号\n     */\n    private Integer userId;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public OrderDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getUserId() {\n        return userId;\n    }\n\n    public OrderDO setUserId(Integer userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"OrderDO{\" +\n                \"id=\" + id +\n                \", userId=\" + userId +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject;\n\n/**\n * 用户 DO\n */\npublic class UserDO {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/orders/OrderMapper.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.mapper.orders;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic interface OrderMapper {\n\n    OrderDO selectById(@Param(\"id\") Integer id);\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/users/UserMapper.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.mapper.users;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic interface UserMapper {\n\n    UserDO selectById(@Param(\"id\") Integer id);\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderService.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.service;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.mapper.orders.OrderMapper;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.mapper.users.UserMapper;\nimport org.springframework.aop.framework.AopContext;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Propagation;\nimport org.springframework.transaction.annotation.Transactional;\n\n@Service\npublic class OrderService {\n\n    @Autowired\n    private OrderMapper orderMapper;\n    @Autowired\n    private UserMapper userMapper;\n\n    private OrderService self() {\n        return (OrderService) AopContext.currentProxy();\n    }\n\n    public void method01() {\n        // 查询订单\n        OrderDO order = orderMapper.selectById(1);\n        System.out.println(order);\n        // 查询用户\n        UserDO user = userMapper.selectById(1);\n        System.out.println(user);\n    }\n\n    @Transactional // 报错，找不到事务管理器\n    public void method02() {\n        // 查询订单\n        OrderDO order = orderMapper.selectById(1);\n        System.out.println(order);\n        // 查询用户\n        UserDO user = userMapper.selectById(1);\n        System.out.println(user);\n    }\n\n    public void method03() {\n        // 查询订单\n        self().method031();\n        // 查询用户\n        self().method032();\n    }\n\n    @Transactional(transactionManager = DBConstants.TX_MANAGER_ORDERS)\n    public void method031() {\n        OrderDO order = orderMapper.selectById(1);\n        System.out.println(order);\n    }\n\n    @Transactional(transactionManager = DBConstants.TX_MANAGER_USERS)\n    public void method032() {\n        UserDO user = userMapper.selectById(1);\n        System.out.println(user);\n    }\n\n    @Transactional(transactionManager = DBConstants.TX_MANAGER_ORDERS)\n    public void method05() {\n        // 查询订单\n        OrderDO order = orderMapper.selectById(1);\n        System.out.println(order);\n        // 查询用户\n        self().method052();\n    }\n\n    @Transactional(transactionManager = DBConstants.TX_MANAGER_USERS,\n            propagation = Propagation.REQUIRES_NEW)\n    public void method052() {\n        UserDO user = userMapper.selectById(1);\n        System.out.println(user);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    # 订单数据源配置\n    orders:\n      jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n      driver-class-name: com.mysql.jdbc.Driver\n      username: root\n      password:\n    # 用户数据源配置\n    users:\n      jdbc-url: jdbc:mysql://127.0.0.1:3306/test_users?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n      driver-class-name: com.mysql.jdbc.Driver\n      username: root\n      password:\n\n# mybatis 配置内容\n#mybatis:\n#  config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径\n#  type-aliases-package: cn.iocoder.springboot.lab17.dynamicdatasource.dataobject # 配置数据库实体包路径\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/src/main/resources/mapper/orders/OrderMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.springboot.lab17.dynamicdatasource.mapper.orders.OrderMapper\">\n\n    <sql id=\"FIELDS\">\n        id, user_id\n    </sql>\n\n    <select id=\"selectById\" parameterType=\"Integer\" resultType=\"OrderDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM orders\n        WHERE id = #{id}\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/src/main/resources/mapper/users/UserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.springboot.lab17.dynamicdatasource.mapper.users.UserMapper\">\n\n    <sql id=\"FIELDS\">\n        id, username\n    </sql>\n\n    <select id=\"selectById\" parameterType=\"Integer\" resultType=\"UserDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM users\n        WHERE id = #{id}\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/src/main/resources/mybatis-config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE configuration PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n<configuration>\n\n    <settings>\n        <!-- 使用驼峰命名法转换字段。 -->\n        <setting name=\"mapUnderscoreToCamelCase\" value=\"true\"/>\n    </settings>\n\n    <typeAliases>\n        <typeAlias alias=\"Integer\" type=\"java.lang.Integer\"/>\n        <typeAlias alias=\"Long\" type=\"java.lang.Long\"/>\n        <typeAlias alias=\"HashMap\" type=\"java.util.HashMap\"/>\n        <typeAlias alias=\"LinkedHashMap\" type=\"java.util.LinkedHashMap\"/>\n        <typeAlias alias=\"ArrayList\" type=\"java.util.ArrayList\"/>\n        <typeAlias alias=\"LinkedList\" type=\"java.util.LinkedList\"/>\n    </typeAliases>\n\n</configuration>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/src/main/resources/sql/db.sql",
    "content": "CREATE TABLE `users` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',\n  `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_username` (`username`)\n) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n\n\n\nCREATE TABLE `orders` (\n  `id` int(11) DEFAULT NULL COMMENT '订单编号',\n  `user_id` int(16) DEFAULT NULL COMMENT '用户编号'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表';\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/orders/OrderMapperTest.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.mapper.orders;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.Application;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class OrderMapperTest {\n\n    @Autowired\n    private OrderMapper orderMapper;\n\n    @Test\n    public void testSelectById() {\n        OrderDO order = orderMapper.selectById(1);\n        System.out.println(order);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/users/UserMapperTest.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.mapper.users;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.Application;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserMapperTest {\n\n    @Autowired\n    private UserMapper userMapper;\n\n    @Test\n    public void testSelectById() {\n        UserDO user = userMapper.selectById(1);\n        System.out.println(user);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/package-info.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource;\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-mybatis/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderServiceTest.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.service;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class OrderServiceTest {\n\n    @Autowired\n    private OrderService orderService;\n\n    @Test\n    public void testMethod01() {\n        orderService.method01();\n    }\n\n    @Test\n    public void testMethod02() {\n        orderService.method02();\n    }\n\n    @Test\n    public void testMethod03() {\n        orderService.method03();\n    }\n\n    @Test\n    public void testMethod05() {\n        orderService.method05();\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-17-dynamic-datasource-sharding-jdbc-01</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.1</version>\n        </dependency>\n\n        <!-- 实现对 Sharding-JDBC 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.shardingsphere</groupId>\n            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>\n            <version>4.0.0-RC2</version>\n        </dependency>\n\n        <!-- 保证 Spring AOP 相关的依赖包 -->\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-aspects</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/Application.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource;\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.EnableAspectJAutoProxy;\n\n@SpringBootApplication\n@MapperScan(basePackages = \"cn.iocoder.springboot.lab17.dynamicdatasource.mapper\")\n@EnableAspectJAutoProxy(exposeProxy = true) // http://www.voidcn.com/article/p-zddcuyii-bpt.html\npublic class Application {\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/OrderDO.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject;\n\n/**\n * 订单 DO\n */\npublic class OrderDO {\n\n    /**\n     * 订单编号\n     */\n    private Integer id;\n    /**\n     * 用户编号\n     */\n    private Integer userId;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public OrderDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getUserId() {\n        return userId;\n    }\n\n    public OrderDO setUserId(Integer userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"OrderDO{\" +\n                \"id=\" + id +\n                \", userId=\" + userId +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject;\n\n/**\n * 用户 DO\n */\npublic class UserDO {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/OrderMapper.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.mapper;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic interface OrderMapper {\n\n    OrderDO selectById(@Param(\"id\") Integer id);\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/UserMapper.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.mapper;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic interface UserMapper {\n\n    UserDO selectById(@Param(\"id\") Integer id);\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderService.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.service;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.mapper.OrderMapper;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.mapper.UserMapper;\nimport org.springframework.aop.framework.AopContext;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Propagation;\nimport org.springframework.transaction.annotation.Transactional;\n\n@Service\npublic class OrderService {\n\n    @Autowired\n    private OrderMapper orderMapper;\n    @Autowired\n    private UserMapper userMapper;\n\n    private OrderService self() {\n        return (OrderService) AopContext.currentProxy();\n    }\n\n    public void method01() {\n        // 查询订单\n        OrderDO order = orderMapper.selectById(1);\n        System.out.println(order);\n        // 查询用户\n        UserDO user = userMapper.selectById(1);\n        System.out.println(user);\n    }\n\n    @Transactional\n    public void method02() {\n        // 查询订单\n        OrderDO order = orderMapper.selectById(1);\n        System.out.println(order);\n        // 查询用户\n        UserDO user = userMapper.selectById(1);\n        System.out.println(user);\n    }\n\n    public void method03() {\n        // 查询订单\n        self().method031();\n        // 查询用户\n        self().method032();\n    }\n\n    @Transactional // 报错，因为此时获取的是 primary 对应的 DataSource ，即 users 。\n    public void method031() {\n        OrderDO order = orderMapper.selectById(1);\n        System.out.println(order);\n    }\n\n    @Transactional\n    public void method032() {\n        UserDO user = userMapper.selectById(1);\n        System.out.println(user);\n    }\n\n    public void method04() {\n        // 查询订单\n        self().method041();\n        // 查询用户\n        self().method042();\n    }\n\n    @Transactional\n    public void method041() {\n        OrderDO order = orderMapper.selectById(1);\n        System.out.println(order);\n    }\n\n    @Transactional\n    public void method042() {\n        UserDO user = userMapper.selectById(1);\n        System.out.println(user);\n    }\n\n    @Transactional\n    public void method05() {\n        // 查询订单\n        OrderDO order = orderMapper.selectById(1);\n        System.out.println(order);\n        // 查询用户\n        self().method052();\n    }\n\n    @Transactional(propagation = Propagation.REQUIRES_NEW)\n    public void method052() {\n        UserDO user = userMapper.selectById(1);\n        System.out.println(user);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/resources/application.yaml",
    "content": "spring:\n  # ShardingSphere 配置项\n  shardingsphere:\n    datasource:\n      # 所有数据源的名字\n      names: ds-orders, ds-users\n      # 订单 orders 数据源配置\n      ds-orders:\n        type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池\n        driver-class-name: com.mysql.jdbc.Driver\n        jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n        username: root\n        password:\n      # 订单 users 数据源配置\n      ds-users:\n        type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池\n        driver-class-name: com.mysql.jdbc.Driver\n        jdbc-url: jdbc:mysql://127.0.0.1:3306/test_users?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n        username: root\n        password:\n    # 分片规则\n    sharding:\n      tables:\n        # orders 表配置\n        orders:\n          actualDataNodes: ds-orders.orders # 映射到 ds-orders 数据源的 orders 表\n        # users 表配置\n        users:\n          actualDataNodes: ds-users.users # 映射到 ds-users 数据源的 users 表\n\n# mybatis 配置内容\nmybatis:\n  config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径\n  mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址\n  type-aliases-package: cn.iocoder.springboot.lab17.dynamicdatasource.dataobject # 配置数据库实体包路径\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/resources/mapper/OrderMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.springboot.lab17.dynamicdatasource.mapper.OrderMapper\">\n\n    <sql id=\"FIELDS\">\n        id, user_id\n    </sql>\n\n    <select id=\"selectById\" parameterType=\"Integer\" resultType=\"OrderDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM orders\n        WHERE id = #{id}\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/resources/mapper/UserMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.springboot.lab17.dynamicdatasource.mapper.UserMapper\">\n\n    <sql id=\"FIELDS\">\n        id, username\n    </sql>\n\n    <select id=\"selectById\" parameterType=\"Integer\" resultType=\"UserDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM users\n        WHERE id = #{id}\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/resources/mybatis-config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE configuration PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n<configuration>\n\n    <settings>\n        <!-- 使用驼峰命名法转换字段。 -->\n        <setting name=\"mapUnderscoreToCamelCase\" value=\"true\"/>\n    </settings>\n\n    <typeAliases>\n        <typeAlias alias=\"Integer\" type=\"java.lang.Integer\"/>\n        <typeAlias alias=\"Long\" type=\"java.lang.Long\"/>\n        <typeAlias alias=\"HashMap\" type=\"java.util.HashMap\"/>\n        <typeAlias alias=\"LinkedHashMap\" type=\"java.util.LinkedHashMap\"/>\n        <typeAlias alias=\"ArrayList\" type=\"java.util.ArrayList\"/>\n        <typeAlias alias=\"LinkedList\" type=\"java.util.LinkedList\"/>\n    </typeAliases>\n\n</configuration>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/main/resources/sql/db.sql",
    "content": "CREATE TABLE `users` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',\n  `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_username` (`username`)\n) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n\n\n\nCREATE TABLE `orders` (\n  `id` int(11) DEFAULT NULL COMMENT '订单编号',\n  `user_id` int(16) DEFAULT NULL COMMENT '用户编号'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表';\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/test/java/dynamicdatasource/mapper/OrderMapperTest.java",
    "content": "package dynamicdatasource.mapper;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.Application;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.mapper.OrderMapper;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class OrderMapperTest {\n\n    @Autowired\n    private OrderMapper orderMapper;\n\n    @Test\n    public void testSelectById() {\n        OrderDO order = orderMapper.selectById(1);\n        System.out.println(order);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/test/java/dynamicdatasource/mapper/UserMapperTest.java",
    "content": "package dynamicdatasource.mapper;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.Application;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.mapper.UserMapper;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserMapperTest {\n\n    @Autowired\n    private UserMapper userMapper;\n\n    @Test\n    public void testSelectById() {\n        UserDO user = userMapper.selectById(1);\n        System.out.println(user);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-01/src/test/java/dynamicdatasource/service/OrderServiceTest.java",
    "content": "package dynamicdatasource.service;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.Application;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.service.OrderService;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class OrderServiceTest {\n\n    @Autowired\n    private OrderService orderService;\n\n    @Test\n    public void testMethod01() {\n        orderService.method01();\n    }\n\n    @Test\n    public void testMethod02() {\n        orderService.method02();\n    }\n\n    @Test\n    public void testMethod03() {\n        orderService.method03();\n    }\n\n    @Test\n    public void testMethod04() {\n        orderService.method04();\n    }\n\n    @Test\n    public void testMethod05() {\n        orderService.method05();\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-17-dynamic-datasource-sharding-jdbc-02</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.1</version>\n        </dependency>\n\n        <!-- 实现对 Sharding-JDBC 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.shardingsphere</groupId>\n            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>\n            <version>4.0.0-RC2</version>\n        </dependency>\n\n        <!-- 保证 Spring AOP 相关的依赖包 -->\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-aspects</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/Application.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource;\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.EnableAspectJAutoProxy;\n\n@SpringBootApplication\n@MapperScan(basePackages = \"cn.iocoder.springboot.lab17.dynamicdatasource.mapper\")\n@EnableAspectJAutoProxy(exposeProxy = true) // http://www.voidcn.com/article/p-zddcuyii-bpt.html\npublic class Application {\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/OrderDO.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject;\n\n/**\n * 订单 DO\n */\npublic class OrderDO {\n\n    /**\n     * 订单编号\n     */\n    private Integer id;\n    /**\n     * 用户编号\n     */\n    private Integer userId;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public OrderDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getUserId() {\n        return userId;\n    }\n\n    public OrderDO setUserId(Integer userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"OrderDO{\" +\n                \"id=\" + id +\n                \", userId=\" + userId +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/OrderMapper.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.mapper;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic interface OrderMapper {\n\n    OrderDO selectById(@Param(\"id\") Integer id);\n\n    int insert(OrderDO entity);\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderService.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.service;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.mapper.OrderMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\n@Service\npublic class OrderService {\n\n    @Autowired\n    private OrderMapper orderMapper;\n\n    @Transactional\n    public void add(OrderDO order) {\n        // 这里先假模假样的读取一下。读取从库\n        OrderDO exists = orderMapper.selectById(1);\n        System.out.println(exists);\n\n        // 插入订单\n        orderMapper.insert(order);\n\n        // 这里先假模假样的读取一下。读取主库\n        exists = orderMapper.selectById(1);\n        System.out.println(exists);\n    }\n\n    public OrderDO findById(Integer id) {\n        return orderMapper.selectById(id);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/main/resources/application.yaml",
    "content": "spring:\n  # ShardingSphere 配置项\n  shardingsphere:\n    # 数据源配置\n    datasource:\n      # 所有数据源的名字\n      names: ds-master, ds-slave-1, ds-slave-2\n      # 订单 orders 主库的数据源配置\n      ds-master:\n        type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池\n        driver-class-name: com.mysql.jdbc.Driver\n        jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n        username: root\n        password:\n      # 订单 orders 从库数据源配置\n      ds-slave-1:\n        type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池\n        driver-class-name: com.mysql.jdbc.Driver\n        jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders_01?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n        username: root\n        password:\n      # 订单 orders 从库数据源配置\n      ds-slave-2:\n        type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池\n        driver-class-name: com.mysql.jdbc.Driver\n        jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders_02?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n        username: root\n        password:\n    # 读写分离配置，对应 YamlMasterSlaveRuleConfiguration 配置类\n    masterslave:\n      name: ms # 名字，任意，需要保证唯一\n      master-data-source-name: ds-master # 主库数据源\n      slave-data-source-names: ds-slave-1, ds-slave-2 # 从库数据源\n\n# mybatis 配置内容\nmybatis:\n  config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径\n  mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址\n  type-aliases-package: cn.iocoder.springboot.lab17.dynamicdatasource.dataobject # 配置数据库实体包路径\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/main/resources/mapper/OrderMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.springboot.lab17.dynamicdatasource.mapper.OrderMapper\">\n\n    <sql id=\"FIELDS\">\n        id, user_id\n    </sql>\n\n    <select id=\"selectById\" parameterType=\"Integer\" resultType=\"OrderDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM orders\n        WHERE id = #{id}\n    </select>\n\n    <insert id=\"insert\" parameterType=\"OrderDO\" useGeneratedKeys=\"true\" keyProperty=\"id\">\n        INSERT INTO orders (\n          user_id\n        ) VALUES (\n          #{userId}\n        )\n    </insert>\n\n</mapper>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/main/resources/mybatis-config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE configuration PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n<configuration>\n\n    <settings>\n        <!-- 使用驼峰命名法转换字段。 -->\n        <setting name=\"mapUnderscoreToCamelCase\" value=\"true\"/>\n    </settings>\n\n    <typeAliases>\n        <typeAlias alias=\"Integer\" type=\"java.lang.Integer\"/>\n        <typeAlias alias=\"Long\" type=\"java.lang.Long\"/>\n        <typeAlias alias=\"HashMap\" type=\"java.util.HashMap\"/>\n        <typeAlias alias=\"LinkedHashMap\" type=\"java.util.LinkedHashMap\"/>\n        <typeAlias alias=\"ArrayList\" type=\"java.util.ArrayList\"/>\n        <typeAlias alias=\"LinkedList\" type=\"java.util.LinkedList\"/>\n    </typeAliases>\n\n</configuration>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/main/resources/sql/db.sql",
    "content": "CREATE TABLE `users` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',\n  `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_username` (`username`)\n) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n\n\n\nCREATE TABLE `orders` (\n  `id` int(11) DEFAULT NULL COMMENT '订单编号',\n  `user_id` int(16) DEFAULT NULL COMMENT '用户编号'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表';\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/mapper/OrderMapperTest.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.mapper;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.Application;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport org.apache.shardingsphere.api.hint.HintManager;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class OrderMapperTest {\n\n    @Autowired\n    private OrderMapper orderMapper;\n\n    @Test\n    public void testSelectById() { // 测试从库的负载均衡\n        for (int i = 0; i < 10; i++) {\n            OrderDO order = orderMapper.selectById(1);\n            System.out.println(order);\n        }\n    }\n\n    @Test\n    public void testSelectById02() { // 测试强制访问主库\n        try (HintManager hintManager = HintManager.getInstance()) {\n            // 设置强制访问主库\n            hintManager.setMasterRouteOnly();\n            // 执行查询\n            OrderDO order = orderMapper.selectById(1);\n            System.out.println(order);\n        }\n    }\n\n    @Test\n    public void testInsert() { // 插入\n        OrderDO order = new OrderDO();\n        order.setUserId(10);\n        orderMapper.insert(order);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-sharding-jdbc-02/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderServiceTest.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.service;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.Application;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class OrderServiceTest {\n\n    @Autowired\n    private OrderService orderService;\n\n    @Test\n    public void testAdd() {\n        OrderDO order = new OrderDO();\n        order.setUserId(20);\n        orderService.add(order);\n    }\n\n    @Test\n    public void testFindById() {\n        OrderDO order = orderService.findById(1);\n        System.out.println(order);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-springdatajpa/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-17-dynamic-datasource-springdatajpa</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- JPA 相关依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-jpa</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/Application.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.EnableAspectJAutoProxy;\n\n@SpringBootApplication\n@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) // http://www.voidcn.com/article/p-zddcuyii-bpt.html\npublic class Application {\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/config/HibernateConfig.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.config;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties;\nimport org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;\nimport org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.util.Map;\n\n@Configuration\npublic class HibernateConfig {\n\n    @Autowired\n    private JpaProperties jpaProperties;\n    @Autowired\n    private HibernateProperties hibernateProperties;\n\n    /**\n     * 获取 Hibernate Vendor 相关配置\n     */\n    @Bean(name = \"hibernateVendorProperties\")\n    public Map<String, Object> hibernateVendorProperties() {\n        return hibernateProperties.determineHibernateProperties(\n                jpaProperties.getProperties(), new HibernateSettings());\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/config/JpaOrdersConfig.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.config;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.boot.jdbc.DataSourceBuilder;\nimport org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.data.jpa.repository.config.EnableJpaRepositories;\nimport org.springframework.orm.jpa.JpaTransactionManager;\nimport org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;\nimport org.springframework.transaction.PlatformTransactionManager;\n\nimport javax.annotation.Resource;\nimport javax.sql.DataSource;\nimport java.util.Map;\n\n@Configuration\n@EnableJpaRepositories(\n        entityManagerFactoryRef = DBConstants.ENTITY_MANAGER_FACTORY_ORDERS,\n        transactionManagerRef = DBConstants.TX_MANAGER_ORDERS,\n        basePackages = {\"cn.iocoder.springboot.lab17.dynamicdatasource.repository.orders\"}) // 设置 Repository 接口所在包\npublic class JpaOrdersConfig {\n\n    @Resource(name = \"hibernateVendorProperties\")\n    private Map<String, Object> hibernateVendorProperties;\n\n    /**\n     * 创建 orders 数据源\n     */\n    @Bean(name = \"ordersDataSource\")\n    @ConfigurationProperties(prefix = \"spring.datasource.orders\")\n    @Primary // 需要特殊添加，否则初始化会有问题\n    public DataSource dataSource() {\n        return DataSourceBuilder.create().build();\n    }\n\n    /**\n     * 创建 LocalContainerEntityManagerFactoryBean\n     */\n    @Bean(name = DBConstants.ENTITY_MANAGER_FACTORY_ORDERS)\n    public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {\n        return builder\n                .dataSource(this.dataSource()) // 数据源\n                .properties(hibernateVendorProperties) // 获取并注入 Hibernate Vendor 相关配置\n                .packages(\"cn.iocoder.springboot.lab17.dynamicdatasource.dataobject\") // 数据库实体 entity 所在包\n                .persistenceUnit(\"ordersPersistenceUnit\") // 设置持久单元的名字，需要唯一\n                .build();\n    }\n\n    /**\n     * 创建 PlatformTransactionManager\n     */\n    @Bean(name = DBConstants.TX_MANAGER_ORDERS)\n    public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {\n        return new JpaTransactionManager(entityManagerFactory(builder).getObject());\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/config/JpaUsersConfig.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.config;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.boot.jdbc.DataSourceBuilder;\nimport org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.jpa.repository.config.EnableJpaRepositories;\nimport org.springframework.orm.jpa.JpaTransactionManager;\nimport org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;\nimport org.springframework.transaction.PlatformTransactionManager;\n\nimport javax.annotation.Resource;\nimport javax.sql.DataSource;\nimport java.util.Map;\n\n@Configuration\n@EnableJpaRepositories(\n        entityManagerFactoryRef = DBConstants.ENTITY_MANAGER_FACTORY_USERS,\n        transactionManagerRef = DBConstants.TX_MANAGER_USERS,\n        basePackages = {\"cn.iocoder.springboot.lab17.dynamicdatasource.repository.users\"}) // 设置 Repository 接口所在包\npublic class JpaUsersConfig {\n\n    @Resource(name = \"hibernateVendorProperties\")\n    private Map<String, Object> hibernateVendorProperties;\n\n    /**\n     * 创建 users 数据源\n     */\n    @Bean(name = \"usersDataSource\")\n    @ConfigurationProperties(prefix = \"spring.datasource.users\")\n    public DataSource dataSource() {\n        return DataSourceBuilder.create().build();\n    }\n\n    /**\n     * 创建 LocalContainerEntityManagerFactoryBean\n     */\n    @Bean(name = DBConstants.ENTITY_MANAGER_FACTORY_USERS)\n    public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder) {\n        return builder\n                .dataSource(this.dataSource()) // 数据源\n                .properties(hibernateVendorProperties) // 获取并注入 Hibernate Vendor 相关配置\n                .packages(\"cn.iocoder.springboot.lab17.dynamicdatasource.dataobject\") // 数据库实体 entity 所在包\n                .persistenceUnit(\"usersPersistenceUnit\") // 设置持久单元的名字，需要唯一\n                .build();\n    }\n\n    /**\n     * 创建 PlatformTransactionManager\n     */\n    @Bean(name = DBConstants.TX_MANAGER_USERS)\n    public PlatformTransactionManager transactionManager(EntityManagerFactoryBuilder builder) {\n        return new JpaTransactionManager(entityManagerFactory(builder).getObject());\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/constant/DBConstants.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.constant;\n\n/**\n * 数据库枚举类\n */\npublic class DBConstants {\n\n    /**\n     * 事务管理器 - 订单库\n     */\n    public static final String TX_MANAGER_ORDERS = \"ordersTransactionManager\";\n    /**\n     * 事务管理器 - 用户库\n     */\n    public static final String TX_MANAGER_USERS = \"usersTransactionManager\";\n\n    /**\n     * 实体管理器工厂 - 订单库\n     */\n    public static final String ENTITY_MANAGER_FACTORY_ORDERS = \"ordersEntityManagerFactory\";\n    /**\n     * 实体管理器工厂 - 用户库\n     */\n    public static final String ENTITY_MANAGER_FACTORY_USERS = \"usersEntityManagerFactory\";\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/OrderDO.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject;\n\nimport javax.persistence.*;\n\n/**\n * 订单 DO\n */\n@Entity\n@Table(name = \"orders\")\npublic class OrderDO {\n\n    /**\n     * 订单编号\n     */\n    @Id\n    @GeneratedValue(strategy = GenerationType.IDENTITY,  // strategy 设置使用数据库主键自增策略；\n            generator = \"JDBC\") // generator 设置插入完成后，查询最后生成的 ID 填充到该属性中。\n    private Integer id;\n    /**\n     * 用户编号\n     */\n    @Column(name = \"user_id\")\n    private Integer userId;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public OrderDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getUserId() {\n        return userId;\n    }\n\n    public OrderDO setUserId(Integer userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"OrderDO{\" +\n                \"id=\" + id +\n                \", userId=\" + userId +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.dataobject;\n\nimport javax.persistence.*;\n\n/**\n * 用户 DO\n */\n@Entity\n@Table(name = \"users\")\npublic class UserDO {\n\n    /**\n     * 用户编号\n     */\n    @Id\n    @GeneratedValue(strategy = GenerationType.IDENTITY,  // strategy 设置使用数据库主键自增策略；\n            generator = \"JDBC\") // generator 设置插入完成后，查询最后生成的 ID 填充到该属性中。\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/repository/orders/OrderRepository.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.repository.orders;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport org.springframework.data.repository.CrudRepository;\n\npublic interface OrderRepository extends CrudRepository<OrderDO, Integer> {\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/repository/users/UserRepository.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.repository.users;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO;\nimport org.springframework.data.repository.CrudRepository;\n\npublic interface UserRepository extends CrudRepository<UserDO, Integer> {\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderService.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.service;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.constant.DBConstants;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.repository.orders.OrderRepository;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.repository.users.UserRepository;\nimport org.springframework.aop.framework.AopContext;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Propagation;\nimport org.springframework.transaction.annotation.Transactional;\n\n@Service\npublic class OrderService {\n\n    @Autowired\n    private OrderRepository orderRepository;\n    @Autowired\n    private UserRepository userRepository;\n\n    private OrderService self() {\n        return (OrderService) AopContext.currentProxy();\n    }\n\n    public void method01() {\n        // 查询订单\n        OrderDO order = orderRepository.findById(1).orElse(null);\n        System.out.println(order);\n        // 查询用户\n        UserDO user = userRepository.findById(1).orElse(null);\n        System.out.println(user);\n    }\n\n    @Transactional // 报错，找不到事务管理器\n    public void method02() {\n        // 查询订单\n        OrderDO order = orderRepository.findById(1).orElse(null);\n        System.out.println(order);\n        // 查询用户\n        UserDO user = userRepository.findById(1).orElse(null);\n        System.out.println(user);\n    }\n\n    public void method03() {\n        // 查询订单\n        self().method031();\n        // 查询用户\n        self().method032();\n    }\n\n    @Transactional(transactionManager = DBConstants.TX_MANAGER_ORDERS)\n    public void method031() {\n        OrderDO order = orderRepository.findById(1).orElse(null);\n        System.out.println(order);\n    }\n\n    @Transactional(transactionManager = DBConstants.TX_MANAGER_USERS)\n    public void method032() {\n        UserDO user = userRepository.findById(1).orElse(null);\n        System.out.println(user);\n    }\n\n    @Transactional(transactionManager = DBConstants.TX_MANAGER_ORDERS)\n    public void method05() {\n        // 查询订单\n        OrderDO order = orderRepository.findById(1).orElse(null);\n        System.out.println(order);\n        // 查询用户\n        self().method052();\n    }\n\n    @Transactional(transactionManager = DBConstants.TX_MANAGER_USERS,\n            propagation = Propagation.REQUIRES_NEW)\n    public void method052() {\n        UserDO user = userRepository.findById(1).orElse(null);\n        System.out.println(user);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    # 订单数据源配置\n    orders:\n      jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n      driver-class-name: com.mysql.jdbc.Driver\n      username: root\n      password:\n    # 用户数据源配置\n    users:\n      jdbc-url: jdbc:mysql://127.0.0.1:3306/test_users?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n      driver-class-name: com.mysql.jdbc.Driver\n      username: root\n      password:\n  jpa:\n    show-sql: true # 打印 SQL 。生产环境，建议关闭\n    # Hibernate 配置内容，对应 HibernateProperties 类\n    hibernate:\n      ddl-auto: none\n\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-springdatajpa/src/main/resources/sql/db.sql",
    "content": "CREATE TABLE `users` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',\n  `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_username` (`username`)\n) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n\n\n\nCREATE TABLE `orders` (\n  `id` int(11) DEFAULT NULL COMMENT '订单编号',\n  `user_id` int(16) DEFAULT NULL COMMENT '用户编号'\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表';\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-springdatajpa/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/package-info.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource;\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-springdatajpa/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/repository/orders/OrderRepositoryTest.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.repository.orders;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.OrderDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class OrderRepositoryTest {\n\n    @Autowired\n    private OrderRepository orderRepository;\n\n    @Test\n    public void testSelectById() {\n        OrderDO order = orderRepository.findById(1)\n                .orElse(null); // 为空，则返回 null\n        System.out.println(order);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-springdatajpa/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/repository/users/UserRepositoryTest.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.repository.users;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.dataobject.UserDO;\nimport cn.iocoder.springboot.lab17.dynamicdatasource.repository.users.UserRepository;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class UserRepositoryTest {\n\n    @Autowired\n    private UserRepository userRepository;\n\n    @Test\n    public void testSelectById() {\n        UserDO user = userRepository.findById(1)\n                .orElse(null); // 为空，则返回 null\n        System.out.println(user);\n    }\n\n}\n"
  },
  {
    "path": "lab-17/lab-17-dynamic-datasource-springdatajpa/src/test/java/cn/iocoder/springboot/lab17/dynamicdatasource/service/OrderServiceTest.java",
    "content": "package cn.iocoder.springboot.lab17.dynamicdatasource.service;\n\nimport cn.iocoder.springboot.lab17.dynamicdatasource.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class OrderServiceTest {\n\n    @Autowired\n    private OrderService orderService;\n\n    @Test\n    public void testMethod01() {\n        orderService.method01();\n    }\n\n    @Test\n    public void testMethod02() {\n        orderService.method02();\n    }\n\n    @Test\n    public void testMethod03() {\n        orderService.method03();\n    }\n\n    @Test\n    public void testMethod05() {\n        orderService.method05();\n    }\n\n}\n"
  },
  {
    "path": "lab-17/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-17</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-17-dynamic-datasource-baomidou-01</module>\n        <module>lab-17-dynamic-datasource-baomidou-02</module>\n        <module>lab-17-dynamic-datasource-mybatis</module>\n        <module>lab-17-dynamic-datasource-springdatajpa</module>\n        <module>lab-17-dynamic-datasource-jdbctemplate</module>\n        <module>lab-17-dynamic-datasource-sharding-jdbc-01</module>\n        <module>lab-17-dynamic-datasource-sharding-jdbc-02</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-17/《芋道 Spring Boot 多数据源（读写分离）入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/dynamic-datasource/?github>\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-18-sharding-datasource-01</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.1</version>\n        </dependency>\n\n        <!-- 实现对 Sharding-JDBC 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.shardingsphere</groupId>\n            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>\n            <version>4.0.0-RC2</version>\n        </dependency>\n\n        <!-- 保证 Spring AOP 相关的依赖包 -->\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-aspects</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-01/src/main/java/cn/iocoder/springboot/lab18/shardingdatasource/Application.java",
    "content": "package cn.iocoder.springboot.lab18.shardingdatasource;\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@MapperScan(basePackages = \"cn.iocoder.springboot.lab18.shardingdatasource.mapper\")\npublic class Application {\n}\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-01/src/main/java/cn/iocoder/springboot/lab18/shardingdatasource/dataobject/OrderConfigDO.java",
    "content": "package cn.iocoder.springboot.lab18.shardingdatasource.dataobject;\n\n/**\n * 订单配置 DO\n */\npublic class OrderConfigDO {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n    /**\n     * 支付超时时间\n     *\n     * 单位：分钟\n     */\n    private Integer payTimeout;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public OrderConfigDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getPayTimeout() {\n        return payTimeout;\n    }\n\n    public OrderConfigDO setPayTimeout(Integer payTimeout) {\n        this.payTimeout = payTimeout;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-01/src/main/java/cn/iocoder/springboot/lab18/shardingdatasource/dataobject/OrderDO.java",
    "content": "package cn.iocoder.springboot.lab18.shardingdatasource.dataobject;\n\n/**\n * 订单 DO\n */\npublic class OrderDO {\n\n    /**\n     * 订单编号\n     */\n    private Long id;\n    /**\n     * 用户编号\n     */\n    private Integer userId;\n\n    public Long getId() {\n        return id;\n    }\n\n    public OrderDO setId(Long id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getUserId() {\n        return userId;\n    }\n\n    public OrderDO setUserId(Integer userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"OrderDO{\" +\n                \"id=\" + id +\n                \", userId=\" + userId +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-01/src/main/java/cn/iocoder/springboot/lab18/shardingdatasource/mapper/OrderConfigMapper.java",
    "content": "package cn.iocoder.springboot.lab18.shardingdatasource.mapper;\n\nimport cn.iocoder.springboot.lab18.shardingdatasource.dataobject.OrderConfigDO;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic interface OrderConfigMapper {\n\n    OrderConfigDO selectById(@Param(\"id\") Integer id);\n\n}\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-01/src/main/java/cn/iocoder/springboot/lab18/shardingdatasource/mapper/OrderMapper.java",
    "content": "package cn.iocoder.springboot.lab18.shardingdatasource.mapper;\n\nimport cn.iocoder.springboot.lab18.shardingdatasource.dataobject.OrderDO;\nimport org.apache.ibatis.annotations.Param;\nimport org.springframework.stereotype.Repository;\n\nimport java.util.List;\n\n@Repository\npublic interface OrderMapper {\n\n    OrderDO selectById(@Param(\"id\") Integer id);\n\n    List<OrderDO> selectListByUserId(@Param(\"userId\") Integer userId);\n\n    void insert(OrderDO order);\n\n}\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-01/src/main/resources/application.yaml",
    "content": "spring:\n  # ShardingSphere 配置项\n  shardingsphere:\n    datasource:\n      # 所有数据源的名字\n      names: ds-orders-0, ds-orders-1\n      # 订单 orders 数据源配置 00\n      ds-orders-0:\n        type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池\n        driver-class-name: com.mysql.jdbc.Driver\n        jdbc-url: jdbc:mysql://127.0.0.1:3306/lab18_orders_0?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n        username: root\n        password:\n      # 订单 orders 数据源配置 01\n      ds-orders-1:\n        type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池\n        driver-class-name: com.mysql.jdbc.Driver\n        jdbc-url: jdbc:mysql://127.0.0.1:3306/lab18_orders_1?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n        username: root\n        password:\n    # 分片规则\n    sharding:\n      tables:\n        # orders 表配置\n        orders:\n#          actualDataNodes: ds-orders-$->{0..1}.orders_$->{0..4} # 映射到 ds-orders 数据源的 orders 表\n#          actualDataNodes: ds-orders-0.orders_0, ds-orders-0.orders_2, ds-orders-0.orders_4, ds-orders-0.orders_6, ds-orders-1.orders_1, ds-orders-1.orders_3, ds-orders-1.orders_5, ds-orders-1.orders_7\n          actualDataNodes: ds-orders-0.orders_$->{[0,2,4,6]}, ds-orders-1.orders_$->{[1,3,5,7]} # 映射到 ds-orders-0 和 ds-orders-1 数据源的 orders 表们\n          key-generator: # 主键生成策略\n            column: id\n            type: SNOWFLAKE\n          database-strategy:\n            inline:\n              algorithm-expression: ds-orders-$->{user_id % 2}\n              sharding-column: user_id\n          table-strategy:\n            inline:\n              algorithm-expression: orders_$->{user_id % 8}\n              sharding-column: user_id\n        # order_config 表配置\n        order_config:\n          actualDataNodes: ds-orders-0.order_config # 仅映射到 ds-orders-0 数据源的 order_config 表\n    # 拓展属性配置\n    props:\n      sql:\n        show: true # 打印 SQL\n\n# mybatis 配置内容\nmybatis:\n  config-location: classpath:mybatis-config.xml # 配置 MyBatis 配置文件路径\n  mapper-locations: classpath:mapper/*.xml # 配置 Mapper XML 地址\n  type-aliases-package: cn.iocoder.springboot.lab18.shardingdatasource.dataobject # 配置数据库实体包路径\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-01/src/main/resources/mapper/OrderConfigMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.springboot.lab18.shardingdatasource.mapper.OrderConfigMapper\">\n\n    <sql id=\"FIELDS\">\n        id, pay_timeout\n    </sql>\n\n    <select id=\"selectById\" parameterType=\"Integer\" resultType=\"OrderConfigDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM order_config\n        WHERE id = #{id}\n    </select>\n\n</mapper>\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-01/src/main/resources/mapper/OrderMapper.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">\n<mapper namespace=\"cn.iocoder.springboot.lab18.shardingdatasource.mapper.OrderMapper\">\n\n    <sql id=\"FIELDS\">\n        id, user_id\n    </sql>\n\n    <select id=\"selectById\" parameterType=\"Integer\" resultType=\"OrderDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM orders\n        WHERE id = #{id}\n    </select>\n\n    <select id=\"selectListByUserId\" parameterType=\"Integer\" resultType=\"OrderDO\">\n        SELECT\n            <include refid=\"FIELDS\" />\n        FROM orders\n        WHERE user_id = #{userId}\n    </select>\n\n    <insert id=\"insert\" parameterType=\"OrderDO\" useGeneratedKeys=\"true\" keyProperty=\"id\">\n        INSERT INTO orders (\n            user_id\n        ) VALUES (\n            #{userId}\n        )\n    </insert>\n\n</mapper>\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-01/src/main/resources/mybatis-config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE configuration PUBLIC \"-//mybatis.org//DTD Config 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-config.dtd\">\n<configuration>\n\n    <settings>\n        <!-- 使用驼峰命名法转换字段。 -->\n        <setting name=\"mapUnderscoreToCamelCase\" value=\"true\"/>\n    </settings>\n\n    <typeAliases>\n        <typeAlias alias=\"Integer\" type=\"java.lang.Integer\"/>\n        <typeAlias alias=\"Long\" type=\"java.lang.Long\"/>\n        <typeAlias alias=\"HashMap\" type=\"java.util.HashMap\"/>\n        <typeAlias alias=\"LinkedHashMap\" type=\"java.util.LinkedHashMap\"/>\n        <typeAlias alias=\"ArrayList\" type=\"java.util.ArrayList\"/>\n        <typeAlias alias=\"LinkedList\" type=\"java.util.LinkedList\"/>\n    </typeAliases>\n\n</configuration>\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-01/src/main/resources/sql/lab18_orders_0.sql",
    "content": "SET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\n-- ----------------------------\n-- Table structure for orders_0\n-- ----------------------------\nDROP TABLE IF EXISTS `orders_0`;\nCREATE TABLE `orders_0` (\n  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '订单编号',\n  `user_id` int(16) DEFAULT NULL COMMENT '用户编号',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表';\n\n-- ----------------------------\n-- Table structure for orders_2\n-- ----------------------------\nDROP TABLE IF EXISTS `orders_2`;\nCREATE TABLE `orders_2` (\n  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '订单编号',\n  `user_id` int(16) DEFAULT NULL COMMENT '用户编号',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表';\n\n-- ----------------------------\n-- Table structure for orders_4\n-- ----------------------------\nDROP TABLE IF EXISTS `orders_4`;\nCREATE TABLE `orders_4` (\n  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '订单编号',\n  `user_id` int(16) DEFAULT NULL COMMENT '用户编号',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表';\n\n-- ----------------------------\n-- Table structure for orders_6\n-- ----------------------------\nDROP TABLE IF EXISTS `orders_6`;\nCREATE TABLE `orders_6` (\n  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '订单编号',\n  `user_id` int(16) DEFAULT NULL COMMENT '用户编号',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表';\n\n\n-- ----------------------------\n-- Table structure for order_config\n-- ----------------------------\nDROP TABLE IF EXISTS `order_config`;\nCREATE TABLE `order_config` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',\n  `pay_timeout` int(11) DEFAULT NULL COMMENT '支付超时时间;单位：分钟',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4;\n\nSET FOREIGN_KEY_CHECKS = 1;\n\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-01/src/main/resources/sql/lab18_orders_1.sql",
    "content": "SET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS = 0;\n\n-- ----------------------------\n-- Table structure for orders_1\n-- ----------------------------\nDROP TABLE IF EXISTS `orders_1`;\nCREATE TABLE `orders_1` (\n  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '订单编号',\n  `user_id` int(16) DEFAULT NULL COMMENT '用户编号',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=400675304294580226 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表';\n\n-- ----------------------------\n-- Table structure for orders_3\n-- ----------------------------\nDROP TABLE IF EXISTS `orders_3`;\nCREATE TABLE `orders_3` (\n  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '订单编号',\n  `user_id` int(16) DEFAULT NULL COMMENT '用户编号',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表';\n\n-- ----------------------------\n-- Table structure for orders_5\n-- ----------------------------\nDROP TABLE IF EXISTS `orders_5`;\nCREATE TABLE `orders_5` (\n  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '订单编号',\n  `user_id` int(16) DEFAULT NULL COMMENT '用户编号',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表';\n\n-- ----------------------------\n-- Table structure for orders_7\n-- ----------------------------\nDROP TABLE IF EXISTS `orders_7`;\nCREATE TABLE `orders_7` (\n  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '订单编号',\n  `user_id` int(16) DEFAULT NULL COMMENT '用户编号',\n  PRIMARY KEY (`id`)\n) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='订单表';\n\nSET FOREIGN_KEY_CHECKS = 1;\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-01/src/test/java/cn/iocoder/springboot/lab18/shardingdatasource/mapper/OrderConfigMapperTest.java",
    "content": "package cn.iocoder.springboot.lab18.shardingdatasource.mapper;\n\nimport cn.iocoder.springboot.lab18.shardingdatasource.Application;\nimport cn.iocoder.springboot.lab18.shardingdatasource.dataobject.OrderConfigDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class OrderConfigMapperTest {\n\n    @Autowired\n    private OrderConfigMapper orderConfigMapper;\n\n    @Test\n    public void testSelectById() {\n        OrderConfigDO orderConfig = orderConfigMapper.selectById(1);\n        System.out.println(orderConfig);\n    }\n\n}\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-01/src/test/java/cn/iocoder/springboot/lab18/shardingdatasource/mapper/OrderMapperTest.java",
    "content": "package cn.iocoder.springboot.lab18.shardingdatasource.mapper;\n\nimport cn.iocoder.springboot.lab18.shardingdatasource.Application;\nimport cn.iocoder.springboot.lab18.shardingdatasource.dataobject.OrderDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.List;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class OrderMapperTest {\n\n    @Autowired\n    private OrderMapper orderMapper;\n\n    @Test\n    public void testSelectById() {\n        OrderDO order = orderMapper.selectById(1);\n        System.out.println(order);\n    }\n\n    @Test\n    public void testSelectListByUserId() {\n        List<OrderDO> orders = orderMapper.selectListByUserId(1);\n        System.out.println(orders.size());\n    }\n\n    @Test\n    public void testInsert() {\n        OrderDO order = new OrderDO();\n        order.setUserId(1);\n        orderMapper.insert(order);\n    }\n\n}\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-02/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-18-sharding-datasource-02</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 Sharding-JDBC 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.shardingsphere</groupId>\n            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>\n            <version>4.0.0-RC2</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis Plus 的自动化配置 -->\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus-boot-starter</artifactId>\n            <version>3.2.0</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-02/src/main/java/cn/iocoder/springboot/lab18/shardingdatasource/Application.java",
    "content": "package cn.iocoder.springboot.lab18.shardingdatasource;\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@MapperScan(basePackages = \"cn.iocoder.springboot.lab18.shardingdatasource.mapper\")\npublic class Application {\n}\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-02/src/main/java/cn/iocoder/springboot/lab18/shardingdatasource/dataobject/OrderDO.java",
    "content": "package cn.iocoder.springboot.lab18.shardingdatasource.dataobject;\n\nimport com.baomidou.mybatisplus.annotation.TableName;\n\n/**\n * 订单 DO\n */\n@TableName(value = \"orders\")\npublic class OrderDO {\n\n    /**\n     * 订单编号\n     */\n    private Long id;\n    /**\n     * 用户编号\n     */\n    private Integer userId;\n\n    public Long getId() {\n        return id;\n    }\n\n    public OrderDO setId(Long id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getUserId() {\n        return userId;\n    }\n\n    public OrderDO setUserId(Integer userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"OrderDO{\" +\n                \"id=\" + id +\n                \", userId=\" + userId +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-02/src/main/java/cn/iocoder/springboot/lab18/shardingdatasource/mapper/OrderMapper.java",
    "content": "package cn.iocoder.springboot.lab18.shardingdatasource.mapper;\n\nimport cn.iocoder.springboot.lab18.shardingdatasource.dataobject.OrderDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic interface OrderMapper extends BaseMapper<OrderDO> {\n\n}\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-02/src/main/java/cn/iocoder/springboot/lab18/shardingdatasource/service/OrderService.java",
    "content": "package cn.iocoder.springboot.lab18.shardingdatasource.service;\n\nimport cn.iocoder.springboot.lab18.shardingdatasource.dataobject.OrderDO;\nimport cn.iocoder.springboot.lab18.shardingdatasource.mapper.OrderMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\n@Service\npublic class OrderService {\n\n    @Autowired\n    private OrderMapper orderMapper;\n\n    @Transactional\n    public void add(OrderDO order) {\n        // 这里先假模假样的读取一下。读取从库\n        OrderDO exists = orderMapper.selectById(1);\n        System.out.println(exists);\n\n        // 插入订单\n        orderMapper.insert(order);\n\n        // 这里先假模假样的读取一下。读取主库\n        exists = orderMapper.selectById(1);\n        System.out.println(exists);\n    }\n\n    public OrderDO findById(Integer id) {\n        return orderMapper.selectById(id);\n    }\n\n}\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-02/src/main/resources/application.yaml",
    "content": "spring:\n  # ShardingSphere 配置项\n  shardingsphere:\n    # 数据源配置\n    datasource:\n      # 所有数据源的名字\n      names: ds-master, ds-slave-1, ds-slave-2\n      # 订单 orders 主库的数据源配置\n      ds-master:\n        type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池\n        driver-class-name: com.mysql.jdbc.Driver\n        jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n        username: root\n        password:\n      # 订单 orders 从库数据源配置\n      ds-slave-1:\n        type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池\n        driver-class-name: com.mysql.jdbc.Driver\n        jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders_01?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n        username: root\n        password:\n      # 订单 orders 从库数据源配置\n      ds-slave-2:\n        type: com.zaxxer.hikari.HikariDataSource # 使用 Hikari 数据库连接池\n        driver-class-name: com.mysql.jdbc.Driver\n        jdbc-url: jdbc:mysql://127.0.0.1:3306/test_orders_02?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n        username: root\n        password:\n    # 读写分离配置，对应 YamlMasterSlaveRuleConfiguration 配置类\n    masterslave:\n      name: ms # 名字，任意，需要保证唯一\n      master-data-source-name: ds-master # 主库数据源\n      slave-data-source-names: ds-slave-1, ds-slave-2 # 从库数据源\n    # 拓展属性配置\n    props:\n      sql:\n        show: true # 打印 SQL\n\n# mybatis-plus 配置内容\nmybatis-plus:\n  configuration:\n    map-underscore-to-camel-case: true # 虽然默认为 true ，但是还是显示去指定下。\n  global-config:\n    db-config:\n      id-type: none # 虽然 MyBatis Plus 也提供 ID 生成策略，但是还是使用 Sharding-JDBC 的\n      logic-delete-value: 1 # 逻辑已删除值(默认为 1)\n      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)\n  mapper-locations: classpath*:mapper/*.xml\n  type-aliases-package: cn.iocoder.springboot.lab18.shardingdatasource.dataobject\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-02/src/test/java/cn/iocoder/springboot/lab18/shardingdatasource/mapper/OrderMapperTest.java",
    "content": "package cn.iocoder.springboot.lab18.shardingdatasource.mapper;\n\nimport cn.iocoder.springboot.lab18.shardingdatasource.Application;\nimport cn.iocoder.springboot.lab18.shardingdatasource.dataobject.OrderDO;\nimport org.apache.shardingsphere.api.hint.HintManager;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class OrderMapperTest {\n\n    @Autowired\n    private OrderMapper orderMapper;\n\n    @Test\n    public void testSelectById() { // 测试从库的负载均衡\n        for (int i = 0; i < 2; i++) {\n            OrderDO order = orderMapper.selectById(1);\n            System.out.println(order);\n        }\n    }\n\n    @Test\n    public void testSelectById02() { // 测试强制访问主库\n        try (HintManager hintManager = HintManager.getInstance()) {\n            // 设置强制访问主库\n            hintManager.setMasterRouteOnly();\n            // 执行查询\n            OrderDO order = orderMapper.selectById(1);\n            System.out.println(order);\n        }\n    }\n\n    @Test\n    public void testInsert() { // 插入\n        OrderDO order = new OrderDO();\n        order.setUserId(10);\n        orderMapper.insert(order);\n    }\n\n}\n"
  },
  {
    "path": "lab-18/lab-18-sharding-datasource-02/src/test/java/cn/iocoder/springboot/lab18/shardingdatasource/service/OrderServiceTest.java",
    "content": "package cn.iocoder.springboot.lab18.shardingdatasource.service;\n\nimport cn.iocoder.springboot.lab18.shardingdatasource.Application;\nimport cn.iocoder.springboot.lab18.shardingdatasource.dataobject.OrderDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class OrderServiceTest {\n\n    @Autowired\n    private OrderService orderService;\n\n    @Test\n    public void testAdd() {\n        OrderDO order = new OrderDO();\n        order.setUserId(20);\n        orderService.add(order);\n    }\n\n    @Test\n    public void testFindById() {\n        OrderDO order = orderService.findById(1);\n        System.out.println(order);\n    }\n\n}\n"
  },
  {
    "path": "lab-18/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-18</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-18-sharding-datasource-01</module>\n        <module>lab-18-sharding-datasource-02</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-18/《芋道 Spring Boot 分库分表入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/sharding-datasource/?github>\n"
  },
  {
    "path": "lab-19/lab-19-datasource-pool-druid-multiple/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-19-datasource-pool-druid-multiple</artifactId>\n\n    <dependencies>\n        <!-- 保证 Spring JDBC 的依赖健全 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <!-- 实现对 Druid 连接池的自动化配置 -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>druid-spring-boot-starter</artifactId>\n            <version>1.1.21</version>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 Spring MVC 的自动化配置，因为我们需要看看 Druid 的监控功能 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-19/lab-19-datasource-pool-druid-multiple/src/main/java/cn/iocoder/springboot/lab19/datasourcepool/Application.java",
    "content": "package cn.iocoder.springboot.lab19.datasourcepool;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\nimport javax.annotation.Resource;\nimport javax.sql.DataSource;\n\n@SpringBootApplication\npublic class Application implements CommandLineRunner {\n\n    private Logger logger = LoggerFactory.getLogger(Application.class);\n\n    @Resource(name = \"ordersDataSource\")\n    private DataSource ordersDataSource;\n\n    @Resource(name = \"usersDataSource\")\n    private DataSource usersDataSource;\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(Application.class, args);\n    }\n\n    @Override\n    public void run(String... args) {\n        // orders 数据源\n        logger.info(\"[run][获得数据源：{}]\", ordersDataSource.getClass());\n\n        // users 数据源\n        logger.info(\"[run][获得数据源：{}]\", usersDataSource.getClass());\n    }\n\n}\n"
  },
  {
    "path": "lab-19/lab-19-datasource-pool-druid-multiple/src/main/java/cn/iocoder/springboot/lab19/datasourcepool/config/DataSourceConfig.java",
    "content": "package cn.iocoder.springboot.lab19.datasourcepool.config;\n\nimport com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Primary;\n\nimport javax.sql.DataSource;\n\n@Configuration\npublic class DataSourceConfig {\n\n    /**\n     * 创建 orders 数据源\n     */\n    @Primary\n    @Bean(name = \"ordersDataSource\")\n    @ConfigurationProperties(prefix = \"spring.datasource.orders\") // 读取 spring.datasource.orders 配置到 HikariDataSource 对象\n    public DataSource ordersDataSource() {\n        return DruidDataSourceBuilder.create().build();\n    }\n\n    /**\n     * 创建 users 数据源\n     */\n    @Bean(name = \"usersDataSource\")\n    @ConfigurationProperties(prefix = \"spring.datasource.users\")\n    public DataSource usersDataSource() {\n        return DruidDataSourceBuilder.create().build();\n    }\n\n}\n"
  },
  {
    "path": "lab-19/lab-19-datasource-pool-druid-multiple/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    # 订单数据源配置\n    orders:\n      url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n      driver-class-name: com.mysql.jdbc.Driver\n      username: root\n      password:\n      type: com.alibaba.druid.pool.DruidDataSource # 设置类型为 DruidDataSource\n      # Druid 自定义配置，对应 DruidDataSource 中的 setting 方法的属性\n      min-idle: 0 # 池中维护的最小空闲连接数，默认为 0 个。\n      max-active: 20 # 池中最大连接数，包括闲置和使用中的连接，默认为 8 个。\n    # 用户数据源配置\n    users:\n      url: jdbc:mysql://127.0.0.1:3306/test_users?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n      driver-class-name: com.mysql.jdbc.Driver\n      username: root\n      password:\n      type: com.alibaba.druid.pool.DruidDataSource # 设置类型为 DruidDataSource\n      # Druid 自定义配置，对应 DruidDataSource 中的 setting 方法的属性\n      min-idle: 0 # 池中维护的最小空闲连接数，默认为 0 个。\n      max-active: 20 # 池中最大连接数，包括闲置和使用中的连接，默认为 8 个。\n    # Druid 自定已配置\n    druid:\n      # 过滤器配置\n      filter:\n        stat: # 配置 StatFilter ，对应文档 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatFilter\n          log-slow-sql: true # 开启慢查询记录\n          slow-sql-millis: 5000 # 慢 SQL 的标准，单位：毫秒\n      # StatViewServlet 配置\n      stat-view-servlet: # 配置 StatViewServlet ，对应文档 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AE\n        enabled: true # 是否开启 StatViewServlet\n        login-username: yudaoyuanma # 账号\n        login-password: javaniubi # 密码\n"
  },
  {
    "path": "lab-19/lab-19-datasource-pool-druid-single/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-19-datasource-pool-druid-single</artifactId>\n\n    <dependencies>\n        <!-- 保证 Spring JDBC 的依赖健全 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <!-- 实现对 Druid 连接池的自动化配置 -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>druid-spring-boot-starter</artifactId>\n            <version>1.1.21</version>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 Spring MVC 的自动化配置，因为我们需要看看 Druid 的监控功能 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-19/lab-19-datasource-pool-druid-single/src/main/java/cn/iocoder/springboot/lab19/datasourcepool/Application.java",
    "content": "package cn.iocoder.springboot.lab19.datasourcepool;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\nimport javax.sql.DataSource;\n\n@SpringBootApplication\npublic class Application implements CommandLineRunner {\n\n    private Logger logger = LoggerFactory.getLogger(Application.class);\n\n    @Autowired\n    private DataSource dataSource;\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(Application.class, args);\n    }\n\n    @Override\n    public void run(String... args) {\n        logger.info(\"[run][获得数据源：{}]\", dataSource.getClass());\n    }\n\n}\n"
  },
  {
    "path": "lab-19/lab-19-datasource-pool-druid-single/src/main/java/cn/iocoder/springboot/lab19/datasourcepool/controller/DruidStatController.java",
    "content": "package cn.iocoder.springboot.lab19.datasourcepool.controller;\n\nimport com.alibaba.druid.stat.DruidStatManagerFacade;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class DruidStatController {\n\n    @GetMapping(\"/monitor/druid/stat\")\n    @Deprecated\n    public Object druidStat(){\n        // `DruidStatManagerFacade#getDataSourceStatDataList()` 方法，可以获取所有数据源的监控数据。\n        // 除此之外，DruidStatManagerFacade 还提供了一些其他方法，你可以按需选择使用。\n        return DruidStatManagerFacade.getInstance().getDataSourceStatDataList();\n    }\n\n}\n"
  },
  {
    "path": "lab-19/lab-19-datasource-pool-druid-single/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容，对应 DataSourceProperties 配置属性类\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root # 数据库账号\n    password: # 数据库密码\n    type: com.alibaba.druid.pool.DruidDataSource # 设置类型为 DruidDataSource\n    # Druid 自定义配置，对应 DruidDataSource 中的 setting 方法的属性\n    druid:\n      min-idle: 0 # 池中维护的最小空闲连接数，默认为 0 个。\n      max-active: 20 # 池中最大连接数，包括闲置和使用中的连接，默认为 8 个。\n      filter:\n        stat: # 配置 StatFilter ，对应文档 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatFilter\n          log-slow-sql: true # 开启慢查询记录\n          slow-sql-millis: 5000 # 慢 SQL 的标准，单位：毫秒\n      stat-view-servlet: # 配置 StatViewServlet ，对应文档 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_StatViewServlet%E9%85%8D%E7%BD%AE\n        enabled: true # 是否开启 StatViewServlet\n        login-username: yudaoyuanma # 账号\n        login-password: javaniubi # 密码\n"
  },
  {
    "path": "lab-19/lab-19-datasource-pool-hikaricp-multiple/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-19-datasource-pool-hikaricp-multiple</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-19/lab-19-datasource-pool-hikaricp-multiple/src/main/java/cn/iocoder/springboot/lab19/datasourcepool/Application.java",
    "content": "package cn.iocoder.springboot.lab19.datasourcepool;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\nimport javax.annotation.Resource;\nimport javax.sql.DataSource;\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\n@SpringBootApplication\npublic class Application implements CommandLineRunner {\n\n    private Logger logger = LoggerFactory.getLogger(Application.class);\n\n    @Resource(name = \"ordersDataSource\")\n    private DataSource ordersDataSource;\n\n    @Resource(name = \"usersDataSource\")\n    private DataSource usersDataSource;\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(Application.class, args);\n    }\n\n    @Override\n    public void run(String... args) {\n        // orders 数据源\n        try (Connection conn = ordersDataSource.getConnection()) {\n            // 这里，可以做点什么\n            logger.info(\"[run][ordersDataSource 获得连接：{}]\", conn);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n\n        // users 数据源\n        try (Connection conn = usersDataSource.getConnection()) {\n            // 这里，可以做点什么\n            logger.info(\"[run][usersDataSource 获得连接：{}]\", conn);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-19/lab-19-datasource-pool-hikaricp-multiple/src/main/java/cn/iocoder/springboot/lab19/datasourcepool/config/DataSourceConfig.java",
    "content": "package cn.iocoder.springboot.lab19.datasourcepool.config;\n\nimport com.zaxxer.hikari.HikariDataSource;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.util.StringUtils;\n\nimport javax.sql.DataSource;\n\n@Configuration\npublic class DataSourceConfig {\n\n    /**\n     * 创建 orders 数据源的配置对象\n     */\n    @Primary\n    @Bean(name = \"ordersDataSourceProperties\")\n    @ConfigurationProperties(prefix = \"spring.datasource.orders\") // 读取 spring.datasource.orders 配置到 DataSourceProperties 对象\n    public DataSourceProperties ordersDataSourceProperties() {\n        return new DataSourceProperties();\n    }\n\n    /**\n     * 创建 orders 数据源\n     */\n    @Bean(name = \"ordersDataSource\")\n    @ConfigurationProperties(prefix = \"spring.datasource.orders.hikari\") // 读取 spring.datasource.orders 配置到 HikariDataSource 对象\n    public DataSource ordersDataSource() {\n        // 获得 DataSourceProperties 对象\n        DataSourceProperties properties =  this.ordersDataSourceProperties();\n        // 创建 HikariDataSource 对象\n        return createHikariDataSource(properties);\n    }\n\n    /**\n     * 创建 users 数据源的配置对象\n     */\n    @Bean(name = \"usersDataSourceProperties\")\n    @ConfigurationProperties(prefix = \"spring.datasource.users\") // 读取 spring.datasource.users 配置到 DataSourceProperties 对象\n    public DataSourceProperties usersDataSourceProperties() {\n        return new DataSourceProperties();\n    }\n\n    /**\n     * 创建 users 数据源\n     */\n    @Bean(name = \"usersDataSource\")\n    @ConfigurationProperties(prefix = \"spring.datasource.users.hikari\")\n    public DataSource usersDataSource() {\n        // 获得 DataSourceProperties 对象\n        DataSourceProperties properties =  this.usersDataSourceProperties();\n        // 创建 HikariDataSource 对象\n        return createHikariDataSource(properties);\n    }\n\n    private static HikariDataSource createHikariDataSource(DataSourceProperties properties) {\n        // 创建 HikariDataSource 对象\n        HikariDataSource dataSource = properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();\n        // 设置线程池名\n        if (StringUtils.hasText(properties.getName())) {\n            dataSource.setPoolName(properties.getName());\n        }\n        return dataSource;\n    }\n\n//    /**\n//     * 创建 orders 数据源\n//     */\n//    @Bean(name = \"ordersDataSource\")\n//    @ConfigurationProperties(prefix = \"spring.datasource.orders\")\n//    public DataSource ordersDataSource() {\n//        return DataSourceBuilder.create().build();\n//    }\n\n}\n"
  },
  {
    "path": "lab-19/lab-19-datasource-pool-hikaricp-multiple/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    # 订单数据源配置\n    orders:\n      url: jdbc:mysql://127.0.0.1:3306/test_orders?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n      driver-class-name: com.mysql.jdbc.Driver\n      username: root\n      password:\n      # HikariCP 自定义配置，对应 HikariConfig 配置属性类\n      hikari:\n        minimum-idle: 20 # 池中维护的最小空闲连接数，默认为 10 个。\n        maximum-pool-size: 20 # 池中最大连接数，包括闲置和使用中的连接，默认为 10 个。\n    # 用户数据源配置\n    users:\n      url: jdbc:mysql://127.0.0.1:3306/test_users?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n      driver-class-name: com.mysql.jdbc.Driver\n      username: root\n      password:\n      # HikariCP 自定义配置，对应 HikariConfig 配置属性类\n      hikari:\n        minimum-idle: 15 # 池中维护的最小空闲连接数，默认为 10 个。\n        maximum-pool-size: 15 # 池中最大连接数，包括闲置和使用中的连接，默认为 10 个。\n"
  },
  {
    "path": "lab-19/lab-19-datasource-pool-hikaricp-single/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-19-datasource-pool-hikaricp-single</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-19/lab-19-datasource-pool-hikaricp-single/src/main/java/cn/iocoder/springboot/lab19/datasourcepool/Application.java",
    "content": "package cn.iocoder.springboot.lab19.datasourcepool;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\nimport javax.sql.DataSource;\nimport java.sql.Connection;\nimport java.sql.SQLException;\n\n@SpringBootApplication\npublic class Application implements CommandLineRunner {\n\n    private Logger logger = LoggerFactory.getLogger(Application.class);\n\n    @Autowired\n    private DataSource dataSource;\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(Application.class, args);\n    }\n\n    @Override\n    public void run(String... args) {\n        try (Connection conn = dataSource.getConnection()) {\n            // 这里，可以做点什么\n            logger.info(\"[run][获得连接：{}]\", conn);\n        } catch (SQLException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-19/lab-19-datasource-pool-hikaricp-single/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容，对应 DataSourceProperties 配置属性类\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root # 数据库账号\n    password: # 数据库密码\n    # HikariCP 自定义配置，对应 HikariConfig 配置属性类\n    hikari:\n      minimum-idle: 20 # 池中维护的最小空闲连接数，默认为 10 个。\n      maximum-pool-size: 20 # 池中最大连接数，包括闲置和使用中的连接，默认为 10 个。\n"
  },
  {
    "path": "lab-19/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-19</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-19-datasource-pool-hikaricp-single</module>\n        <module>lab-19-datasource-pool-hikaricp-multiple</module>\n        <module>lab-19-datasource-pool-druid-single</module>\n        <module>lab-19-datasource-pool-druid-multiple</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-19/《芋道 Spring Boot 数据库连接池入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/datasource-pool/?github>\n"
  },
  {
    "path": "lab-20/lab-20-database-version-control-flyway/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-20-database-version-control-flyway</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <!-- 同时，spring-boot-starter-jdbc 支持 Flyway 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- Flyway 依赖 -->\n        <dependency>\n            <groupId>org.flywaydb</groupId>\n            <artifactId>flyway-core</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-20/lab-20-database-version-control-flyway/src/main/java/cn/iocoder/springboot/lab20/databaseversioncontrol/Application.java",
    "content": "package cn.iocoder.springboot.lab20.databaseversioncontrol;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-20/lab-20-database-version-control-flyway/src/main/java/cn/iocoder/springboot/lab20/databaseversioncontrol/migration/ExampleFlywayCallback.java",
    "content": "package cn.iocoder.springboot.lab20.databaseversioncontrol.migration;\n\nimport org.flywaydb.core.api.callback.Callback;\nimport org.flywaydb.core.api.callback.Context;\nimport org.flywaydb.core.api.callback.Event;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ExampleFlywayCallback implements Callback {\n\n    private Logger log = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public boolean supports(Event event, Context context) {\n        return true;\n    }\n\n    @Override\n    public boolean canHandleInTransaction(Event event, Context context) {\n        return false;\n    }\n\n    @Override\n    public void handle(Event event, Context context) {\n        log.info(\"event: {}\", event);\n    }\n\n}\n"
  },
  {
    "path": "lab-20/lab-20-database-version-control-flyway/src/main/java/cn/iocoder/springboot/lab20/databaseversioncontrol/migration/V1_1__FixUsername.java",
    "content": "package cn.iocoder.springboot.lab20.databaseversioncontrol.migration;\n\nimport org.flywaydb.core.api.MigrationVersion;\nimport org.flywaydb.core.api.migration.BaseJavaMigration;\nimport org.flywaydb.core.api.migration.Context;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.jdbc.core.RowCallbackHandler;\n\nimport java.sql.ResultSet;\nimport java.sql.SQLException;\n\npublic class V1_1__FixUsername extends BaseJavaMigration {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void migrate(Context context) throws Exception {\n        // 创建 JdbcTemplate ，方便 JDBC 操作\n        JdbcTemplate template = new JdbcTemplate(context.getConfiguration().getDataSource());\n        // 查询所有用户，如果用户名为 yudaoyuanma ，则变更成 yutou\n        template.query(\"SELECT id, username, password, create_time FROM users\", new RowCallbackHandler() {\n            @Override\n            public void processRow(ResultSet rs) throws SQLException {\n                // 遍历返回的结果\n                do {\n                    String username = rs.getString(\"username\");\n                    if (\"yudaoyuanma\".equals(username)) {\n                        Integer id = rs.getInt(\"id\");\n                        template.update(\"UPDATE users SET username = ? WHERE id = ?\",\n                                \"yutou\", id);\n                        logger.info(\"[migrate][更新 user({}) 的用户名({} => {})\", id, username, \"yutou\");\n                    }\n                } while (rs.next());\n            }\n        });\n    }\n\n    @Override\n    public Integer getChecksum() {\n        return 11; // 默认返回，是 null 。\n    }\n\n    @Override\n    public boolean canExecuteInTransaction() {\n        return true; // 默认返回，也是 true\n    }\n\n    @Override\n    public MigrationVersion getVersion() {\n        return super.getVersion(); // 默认按照约定的规则，从类名中解析获得。可以自定义\n    }\n\n}\n"
  },
  {
    "path": "lab-20/lab-20-database-version-control-flyway/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容，对应 DataSourceProperties 配置属性类\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/lab-20-flyway?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root # 数据库账号\n    password: # 数据库密码\n  # flyway 配置内容，对应 FlywayAutoConfiguration.FlywayConfiguration 配置项\n  flyway:\n    enabled: true # 开启 Flyway 功能\n    cleanDisabled: true # 禁用 Flyway 所有的 drop 相关的逻辑，避免出现跑路的情况。\n    locations: # 迁移脚本目录\n      - classpath:db/migration # 配置 SQL-based 的 SQL 脚本在该目录下\n      - classpath:cn.iocoder.springboot.lab20.databaseversioncontrol.migration # 配置 Java-based 的 Java 文件在该目录下\n    check-location: false # 是否校验迁移脚本目录下。如果配置为 true ，代表需要校验。此时，如果目录下没有迁移脚本，会抛出 IllegalStateException 异常\n    url: jdbc:mysql://127.0.0.1:3306/lab-20-flyway?useSSL=false&useUnicode=true&characterEncoding=UTF-8 # 数据库地址\n    user: root # 数据库账号\n    password: # 数据库密码\n"
  },
  {
    "path": "lab-20/lab-20-database-version-control-flyway/src/main/resources/db/migration/V1.0__INIT_DB.sql",
    "content": "-- 创建用户表\nCREATE TABLE `users` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',\n  `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',\n  `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码',\n  `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_username` (`username`)\n) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n\n-- 插入一条数据\nINSERT INTO `users`(username, password, create_time) VALUES('yudaoyuanma', 'password', now());\n"
  },
  {
    "path": "lab-20/lab-20-database-version-control-liquibase/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-20-database-version-control-liquibase</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <!-- 同时，spring-boot-starter-jdbc 支持 Liquibase 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n<!--            <version>5.1.48</version>-->\n        </dependency>\n\n        <!-- Liquibase 依赖 -->\n        <dependency>\n            <groupId>org.liquibase</groupId>\n            <artifactId>liquibase-core</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-20/lab-20-database-version-control-liquibase/src/main/java/cn/iocoder/springboot/lab20/databaseversioncontrol/Application.java",
    "content": "package cn.iocoder.springboot.lab20.databaseversioncontrol;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-20/lab-20-database-version-control-liquibase/src/main/java/cn/iocoder/springboot/lab20/databaseversioncontrol/migration/CHANGE_SET_3_FixUsername.java",
    "content": "package cn.iocoder.springboot.lab20.databaseversioncontrol.migration;\n\nimport liquibase.change.custom.CustomTaskChange;\nimport liquibase.database.Database;\nimport liquibase.database.jvm.JdbcConnection;\nimport liquibase.exception.CustomChangeException;\nimport liquibase.exception.SetupException;\nimport liquibase.exception.ValidationErrors;\nimport liquibase.resource.ResourceAccessor;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.sql.PreparedStatement;\nimport java.sql.ResultSet;\n\npublic class CHANGE_SET_3_FixUsername implements CustomTaskChange {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void execute(Database database) throws CustomChangeException {\n        JdbcConnection connection = (JdbcConnection) database.getConnection();\n        try (PreparedStatement psmt = connection.prepareStatement(\"SELECT id, username, password, create_time FROM users\")) {\n            try (ResultSet rs = psmt.executeQuery()) {\n                while (rs.next()) {\n                    String username = rs.getString(\"username\");\n                    if (\"yudaoyuanma\".equals(username)) {\n                        Integer id = rs.getInt(\"id\");\n                        // 这里，再来一刀更新操作，偷懒不写了。\n                        logger.info(\"[migrate][更新 user({}) 的用户名({} => {})\", id, username, \"yutou\");\n                    }\n                }\n            }\n        } catch (Exception e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override\n    public String getConfirmationMessage() {\n        return null;\n    }\n\n    @Override\n    public void setUp() throws SetupException {\n    }\n\n    @Override\n    public void setFileOpener(ResourceAccessor resourceAccessor) {\n    }\n\n    @Override\n    public ValidationErrors validate(Database database) {\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "lab-20/lab-20-database-version-control-liquibase/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容，对应 DataSourceProperties 配置属性类\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/lab-20-liquibase?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root # 数据库账号\n    password: # 数据库密码\n  # Liquibase 配置内容，对应 LiquibaseProperties 配置项\n  liquibase:\n    enabled: true # 开启 Liquibase 功能。默认为 true 。\n    change-log: classpath:/db/changelog/db.changelog-master.yaml # Liquibase 配置文件地址\n    url: jdbc:mysql://127.0.0.1:3306/lab-20-liquibase?useSSL=false&useUnicode=true&characterEncoding=UTF-8 # 数据库地址\n    user: root # 数据库账号\n    password: # 数据库密码\n"
  },
  {
    "path": "lab-20/lab-20-database-version-control-liquibase/src/main/resources/db/changelog/db.changelog-master-bak.yaml",
    "content": "databaseChangeLog:\n"
  },
  {
    "path": "lab-20/lab-20-database-version-control-liquibase/src/main/resources/db/changelog/db.changelog-master.yaml",
    "content": "databaseChangeLog:\n  - changeSet: # 对应一个 ChangeSet 对象\n      id: 0 # ChangeSet 编号\n      author: yunai # 作者\n      comments: 空 # 备注\n  - changeSet: # 对应一个 ChangeSet 对象\n      id: 1 # ChangeSet 编号\n      author: yunai # 作者\n      comments: 初始化 users 表 # 备注\n      changes: # 对应 Change 数组。Change 是一个接口，每种操作对应一种 Change 实现类\n        - createTable: # 创建表，对应 CreateTableChange 对象。\n            tableName: users # 表名\n            remarkds: 用户表 # 表注释\n            columns: # 对应 ColumnConfig 数组\n              - column:\n                  name: id # 字段名\n                  type: int # 字段类型\n                  autoIncrement: true # 自增\n                  constraints: # 限制条件，对应一个 ConstraintsConfig 对象\n                    primaryKey: true # 主键\n                    nullable: false # 不允许空\n              - column:\n                  name: username\n                  type: varchar(64)\n                  constraints:\n                    nullable: false\n              - column:\n                  name: password\n                  type: varchar(32)\n                  constraints:\n                    nullable: false\n              - column:\n                  name: create_time\n                  type: datetime\n                  constraints:\n                    nullable: false\n        - insert: # 插入记录，对应 InsertDataChange 对象。\n            tableName: users # 表名\n            columns: # 对应 ColumnConfig 数组\n              - column:\n                  name: username # 字段名\n                  value: yudaoyuanma # 值\n              - column:\n                  name: password\n                  value: password\n              - column:\n                  name: create_time\n                  value: now()\n  - changeSet: # 对应一个 ChangeSet 对象\n      id: 2 # ChangeSet 编号\n      author: yunai # 作者\n      comments: 初始化 users2 表 # 备注\n      changes: # 对应 Change 数组。Change 是一个接口，每种操作对应一种 Change 实现类\n        - sqlFile: # 使用 SQL 文件，对应 SQLFileChange 对象\n            encoding: utf8\n            path: classpath:db/changelog/sqlfile/CHAGE_SET_2_INIT_DB.sql\n  - changeSet: # 对应一个 ChangeSet 对象\n      id: 3 # ChangeSet 编号\n      author: yunai # 作者\n      comments: 修复 `users` 的用户名 # 备注\n      changes: # 对应 Change 数组。Change 是一个接口，每种操作对应一种 Change 实现类\n        - customChange: {class: cn.iocoder.springboot.lab20.databaseversioncontrol.migration.CHANGE_SET_3_FixUsername} # 对应 CustomTaskChange\n"
  },
  {
    "path": "lab-20/lab-20-database-version-control-liquibase/src/main/resources/db/changelog/sqlfile/CHAGE_SET_2_INIT_DB.sql",
    "content": "-- 创建用户表\nCREATE TABLE `users2` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',\n  `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',\n  `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码',\n  `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_username` (`username`)\n) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n\n-- 插入一条数据\nINSERT INTO `users2`(username, password, create_time) VALUES('yudaoyuanma', 'password', now());\n"
  },
  {
    "path": "lab-20/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-20</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-20-database-version-control-flyway</module>\n        <module>lab-20-database-version-control-liquibase</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-20/《芋道 Spring Boot 数据库版本管理入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/database-version-control/?gtihub>\n"
  },
  {
    "path": "lab-21/lab-21-cache-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-21-cache-demo</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n\n        <!-- 实现对 MyBatis Plus 的自动化配置 -->\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus-boot-starter</artifactId>\n            <version>3.2.0</version>\n        </dependency>\n\n        <!-- 实现对 Caches 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-cache</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-21/lab-21-cache-demo/src/main/java/cn/iocoder/springboot/lab21/cache/Application.java",
    "content": "package cn.iocoder.springboot.lab21.cache;\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cache.annotation.EnableCaching;\n\n@SpringBootApplication\n@EnableCaching\n@MapperScan(basePackages = \"cn.iocoder.springboot.lab21.cache.mapper\")\npublic class Application {\n}\n"
  },
  {
    "path": "lab-21/lab-21-cache-demo/src/main/java/cn/iocoder/springboot/lab21/cache/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab21.cache.dataobject;\n\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport com.baomidou.mybatisplus.annotation.TableName;\n\nimport java.util.Date;\n\n/**\n * 用户 DO\n */\n@TableName(value = \"users\")\npublic class UserDO {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码（明文）\n     *\n     * ps：生产环境下，千万不要明文噢\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n    /**\n     * 是否删除\n     */\n    @TableLogic\n    private Integer deleted;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    public UserDO setCreateTime(Date createTime) {\n        this.createTime = createTime;\n        return this;\n    }\n\n    public Integer getDeleted() {\n        return deleted;\n    }\n\n    public UserDO setDeleted(Integer deleted) {\n        this.deleted = deleted;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-21/lab-21-cache-demo/src/main/java/cn/iocoder/springboot/lab21/cache/mapper/UserCacheDao.java",
    "content": "package cn.iocoder.springboot.lab21.cache.mapper;\n\nimport cn.iocoder.springboot.lab21.cache.dataobject.UserDO;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic class UserCacheDao {\n\n    public UserDO get(Integer id) {\n        return new UserDO();\n    }\n\n    public void put(UserDO user) {\n    }\n\n}\n"
  },
  {
    "path": "lab-21/lab-21-cache-demo/src/main/java/cn/iocoder/springboot/lab21/cache/mapper/UserMapper.java",
    "content": "package cn.iocoder.springboot.lab21.cache.mapper;\n\nimport cn.iocoder.springboot.lab21.cache.dataobject.UserDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.springframework.cache.annotation.CachePut;\nimport org.springframework.cache.annotation.Cacheable;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic interface UserMapper extends BaseMapper<UserDO> {\n\n    @Cacheable(value = \"users\", key = \"#id\")\n    UserDO selectById(Integer id);\n\n    @CachePut(value = \"users\", key = \"#user.id\")\n    default UserDO insert0(UserDO user) {\n        // 插入记录\n        this.insert(user);\n        // 返回用户\n        return user;\n    }\n\n}\n"
  },
  {
    "path": "lab-21/lab-21-cache-demo/src/main/java/cn/iocoder/springboot/lab21/cache/service/UserService.java",
    "content": "package cn.iocoder.springboot.lab21.cache.service;\n\nimport cn.iocoder.springboot.lab21.cache.dataobject.UserDO;\nimport cn.iocoder.springboot.lab21.cache.mapper.UserCacheDao;\nimport cn.iocoder.springboot.lab21.cache.mapper.UserMapper;\nimport org.springframework.beans.factory.annotation.Autowired;\n\npublic class UserService {\n\n    @Autowired\n    private UserMapper userMapper;\n\n    @Autowired\n    private UserCacheDao userCacheDao;\n\n    public UserDO getUser(Integer id) {\n        // 从 Cache 中，查询用户信息\n        UserDO user = userCacheDao.get(id);\n        if (user != null) {\n            return user;\n        }\n        // 如果 Cache 查询不到，从 DB 中读取\n        user = userMapper.selectById(id);\n        if (user != null) { // 非空，则缓存到 Cache 中\n            userCacheDao.put(user);\n        }\n        // 返回结果\n        return user;\n    }\n\n    public UserDO getUser2(Integer id) {\n        return userMapper.selectById(id);\n    }\n\n}\n"
  },
  {
    "path": "lab-21/lab-21-cache-demo/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/lab-21-cache-demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n# mybatis-plus 配置内容\nmybatis-plus:\n  configuration:\n    map-underscore-to-camel-case: true # 虽然默认为 true ，但是还是显示去指定下。\n  global-config:\n    db-config:\n      id-type: auto # ID 主键自增\n      logic-delete-value: 1 # 逻辑已删除值(默认为 1)\n      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)\n  mapper-locations: classpath*:mapper/*.xml\n  type-aliases-package: cn.iocoder.springboot.lab21.cache.dataobject\n\n# logging\nlogging:\n  level:\n    # dao 开启 debug 模式 mybatis 输入 sql\n    cn:\n      iocoder:\n        springboot:\n          lab21:\n            cache:\n              mapper: debug\n"
  },
  {
    "path": "lab-21/lab-21-cache-demo/src/main/resources/sql/users.sql",
    "content": "CREATE TABLE `users` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',\n  `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',\n  `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码',\n  `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n  `deleted` bit(1) DEFAULT NULL COMMENT '是否删除。0-未删除；1-删除',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_username` (`username`)\n) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n"
  },
  {
    "path": "lab-21/lab-21-cache-demo/src/test/java/cn/iocoder/springboot/lab21/cache/UserMapperTest.java",
    "content": "package cn.iocoder.springboot.lab21.cache;\n\nimport cn.iocoder.springboot.lab21.cache.dataobject.UserDO;\nimport cn.iocoder.springboot.lab21.cache.mapper.UserMapper;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.Date;\nimport java.util.UUID;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserMapperTest {\n\n    @Autowired\n    private UserMapper userMapper;\n\n    @Test\n    public void testSelectById() {\n        UserDO user = userMapper.selectById(2);\n        System.out.println(\"user：\" + user);\n        user = userMapper.selectById(2);\n        System.out.println(\"user：\" + user);\n    }\n\n    @Test\n    public void testInsert () {\n        // 插入记录\n        UserDO user = new UserDO();\n        user.setUsername(UUID.randomUUID().toString()); // 随机账号，因为唯一索引\n        user.setPassword(\"nicai\");\n        user.setCreateTime(new Date());\n        user.setDeleted(0);\n        userMapper.insert0(user);\n\n        // 查询数据\n        user = userMapper.selectById(user.getId());\n        System.out.println(user);\n    }\n\n}\n"
  },
  {
    "path": "lab-21/lab-21-cache-ehcache/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-21-cache-ehcache</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n\n        <!-- 实现对 MyBatis Plus 的自动化配置 -->\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus-boot-starter</artifactId>\n            <version>3.2.0</version>\n        </dependency>\n\n        <!-- 实现对 Caches 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-cache</artifactId>\n        </dependency>\n\n        <!-- Ehcache 依赖 -->\n        <dependency>\n            <groupId>net.sf.ehcache</groupId>\n            <artifactId>ehcache</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-21/lab-21-cache-ehcache/src/main/java/cn/iocoder/springboot/lab21/cache/Application.java",
    "content": "package cn.iocoder.springboot.lab21.cache;\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cache.annotation.EnableCaching;\n\n@SpringBootApplication\n@EnableCaching\n@MapperScan(basePackages = \"cn.iocoder.springboot.lab21.cache.mapper\")\npublic class Application {\n}\n"
  },
  {
    "path": "lab-21/lab-21-cache-ehcache/src/main/java/cn/iocoder/springboot/lab21/cache/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab21.cache.dataobject;\n\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport com.baomidou.mybatisplus.annotation.TableName;\n\nimport java.util.Date;\n\n/**\n * 用户 DO\n */\n@TableName(value = \"users\")\npublic class UserDO {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码（明文）\n     *\n     * ps：生产环境下，千万不要明文噢\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n    /**\n     * 是否删除\n     */\n    @TableLogic\n    private Integer deleted;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    public UserDO setCreateTime(Date createTime) {\n        this.createTime = createTime;\n        return this;\n    }\n\n    public Integer getDeleted() {\n        return deleted;\n    }\n\n    public UserDO setDeleted(Integer deleted) {\n        this.deleted = deleted;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                \", createTime=\" + createTime +\n                \", deleted=\" + deleted +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-21/lab-21-cache-ehcache/src/main/java/cn/iocoder/springboot/lab21/cache/mapper/UserMapper.java",
    "content": "package cn.iocoder.springboot.lab21.cache.mapper;\n\nimport cn.iocoder.springboot.lab21.cache.dataobject.UserDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.springframework.cache.annotation.CacheConfig;\nimport org.springframework.cache.annotation.CacheEvict;\nimport org.springframework.cache.annotation.CachePut;\nimport org.springframework.cache.annotation.Cacheable;\nimport org.springframework.stereotype.Repository;\n\n@Repository\n@CacheConfig(cacheNames = \"users\")\npublic interface UserMapper extends BaseMapper<UserDO> {\n\n    @Cacheable(key = \"#id\")\n    UserDO selectById(Integer id);\n\n    @CachePut(key = \"#user.id\")\n    default UserDO insert0(UserDO user) {\n        // 插入记录\n        this.insert(user);\n        // 返回用户\n        return user;\n    }\n\n    @CacheEvict(key = \"#id\")\n    int deleteById(Integer id);\n\n}\n"
  },
  {
    "path": "lab-21/lab-21-cache-ehcache/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/lab-21-cache-demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n  # cache 缓存配置内容\n  cache:\n    type: ehcache\n\n# mybatis-plus 配置内容\nmybatis-plus:\n  configuration:\n    map-underscore-to-camel-case: true # 虽然默认为 true ，但是还是显示去指定下。\n  global-config:\n    db-config:\n      id-type: auto # ID 主键自增\n      logic-delete-value: 1 # 逻辑已删除值(默认为 1)\n      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)\n  mapper-locations: classpath*:mapper/*.xml\n  type-aliases-package: cn.iocoder.springboot.lab21.cache.dataobject\n\n# logging\nlogging:\n  level:\n    # dao 开启 debug 模式 mybatis 输入 sql\n    cn:\n      iocoder:\n        springboot:\n          lab21:\n            cache:\n              mapper: debug\n"
  },
  {
    "path": "lab-21/lab-21-cache-ehcache/src/main/resources/ehcache.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ehcache xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNamespaceSchemaLocation=\"http://ehcache.org/ehcache.xsd\">\n\n    <!-- users 缓存 -->\n    <!-- name：缓存名 -->\n    <!-- maxElementsInMemory：最大缓存 key 数量 -->\n    <!-- timeToLiveSeconds：缓存过期时长，单位：秒 -->\n    <!-- memoryStoreEvictionPolicy：缓存淘汰策略 -->\n    <cache name=\"users\"\n           maxElementsInMemory=\"1000\"\n           timeToLiveSeconds=\"60\"\n           memoryStoreEvictionPolicy=\"LRU\"/>  <!-- 缓存淘汰策略 -->\n\n</ehcache>\n"
  },
  {
    "path": "lab-21/lab-21-cache-ehcache/src/main/resources/sql/users.sql",
    "content": "CREATE TABLE `users` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',\n  `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',\n  `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码',\n  `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n  `deleted` bit(1) DEFAULT NULL COMMENT '是否删除。0-未删除；1-删除',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_username` (`username`)\n) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n\nINSERT INTO `users`(`id`, `username`, `password`, `create_time`, `deleted`) VALUES (1, 'yudaoyuanma', 'buzhidao', now(), 0);\n"
  },
  {
    "path": "lab-21/lab-21-cache-ehcache/src/test/java/cn/iocoder/springboot/lab21/cache/UserMapperTest.java",
    "content": "package cn.iocoder.springboot.lab21.cache;\n\nimport cn.iocoder.springboot.lab21.cache.dataobject.UserDO;\nimport cn.iocoder.springboot.lab21.cache.mapper.UserMapper;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.cache.CacheManager;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.Date;\nimport java.util.UUID;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserMapperTest {\n\n    private static final String CACHE_NAME_USER = \"users\";\n\n    @Autowired\n    private UserMapper userMapper;\n\n    @Autowired\n    private CacheManager cacheManager;\n\n    @Test\n    public void testCacheManager() {\n        System.out.println(cacheManager);\n    }\n\n    @Test\n    public void testSelectById() {\n        // 这里，胖友事先插入一条 id = 1 的记录。\n        Integer id = 1;\n\n        // 查询 id = 1 的记录\n        UserDO user = userMapper.selectById(id);\n        System.out.println(\"user：\" + user);\n        // 判断缓存中，是不是存在\n        Assert.assertNotNull(\"缓存为空\", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class));\n\n        // 查询 id = 1 的记录\n        user = userMapper.selectById(id);\n        System.out.println(\"user：\" + user);\n    }\n\n    @Test\n    public void testInsert() {\n        // 插入记录\n        UserDO user = new UserDO();\n        user.setUsername(UUID.randomUUID().toString()); // 随机账号，因为唯一索引\n        user.setPassword(\"nicai\");\n        user.setCreateTime(new Date());\n        user.setDeleted(0);\n        userMapper.insert0(user);\n\n        // 判断缓存中，是不是存在\n        Assert.assertNotNull(\"缓存为空\", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class));\n    }\n\n    @Test\n    public void testDeleteById() {\n        // 插入记录，为了让缓存里有记录\n        UserDO user = new UserDO();\n        user.setUsername(UUID.randomUUID().toString()); // 随机账号，因为唯一索引\n        user.setPassword(\"nicai\");\n        user.setCreateTime(new Date());\n        user.setDeleted(0);\n        userMapper.insert0(user);\n        // 判断缓存中，是不是存在\n        Assert.assertNotNull(\"缓存为空\", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class));\n\n        // 删除记录，为了让缓存被删除\n        userMapper.deleteById(user.getId());\n        // 判断缓存中，是不是存在\n        Assert.assertNull(\"缓存不为空\", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class));\n    }\n\n}\n"
  },
  {
    "path": "lab-21/lab-21-cache-redis/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-21-cache-redis</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n        </dependency>\n\n        <!-- 实现对 MyBatis Plus 的自动化配置 -->\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>mybatis-plus-boot-starter</artifactId>\n            <version>3.2.0</version>\n        </dependency>\n\n        <!-- 实现对 Caches 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-cache</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Data Redis 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis</artifactId>\n            <exclusions>\n                <!-- 去掉对 Lettuce 的依赖，因为 Spring Boot 优先使用 Lettuce 作为 Redis 客户端 -->\n                <exclusion>\n                    <groupId>io.lettuce</groupId>\n                    <artifactId>lettuce-core</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n        <!-- 引入 Jedis 的依赖，这样 Spring Boot 实现对 Jedis 的自动化配置 -->\n        <dependency>\n            <groupId>redis.clients</groupId>\n            <artifactId>jedis</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-21/lab-21-cache-redis/src/main/java/cn/iocoder/springboot/lab21/cache/Application.java",
    "content": "package cn.iocoder.springboot.lab21.cache;\n\nimport org.mybatis.spring.annotation.MapperScan;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cache.annotation.EnableCaching;\n\n@SpringBootApplication\n@EnableCaching\n@MapperScan(basePackages = \"cn.iocoder.springboot.lab21.cache.mapper\")\npublic class Application {\n}\n"
  },
  {
    "path": "lab-21/lab-21-cache-redis/src/main/java/cn/iocoder/springboot/lab21/cache/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab21.cache.dataobject;\n\nimport com.baomidou.mybatisplus.annotation.TableLogic;\nimport com.baomidou.mybatisplus.annotation.TableName;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 用户 DO\n */\n@TableName(value = \"users\")\npublic class UserDO implements Serializable {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码（明文）\n     *\n     * ps：生产环境下，千万不要明文噢\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n    /**\n     * 是否删除\n     */\n    @TableLogic\n    private Integer deleted;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    public UserDO setCreateTime(Date createTime) {\n        this.createTime = createTime;\n        return this;\n    }\n\n    public Integer getDeleted() {\n        return deleted;\n    }\n\n    public UserDO setDeleted(Integer deleted) {\n        this.deleted = deleted;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                \", createTime=\" + createTime +\n                \", deleted=\" + deleted +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-21/lab-21-cache-redis/src/main/java/cn/iocoder/springboot/lab21/cache/mapper/UserMapper.java",
    "content": "package cn.iocoder.springboot.lab21.cache.mapper;\n\nimport cn.iocoder.springboot.lab21.cache.dataobject.UserDO;\nimport com.baomidou.mybatisplus.core.mapper.BaseMapper;\nimport org.springframework.cache.annotation.CacheConfig;\nimport org.springframework.cache.annotation.CacheEvict;\nimport org.springframework.cache.annotation.CachePut;\nimport org.springframework.cache.annotation.Cacheable;\nimport org.springframework.stereotype.Repository;\n\n@Repository\n@CacheConfig(cacheNames = \"users\")\npublic interface UserMapper extends BaseMapper<UserDO> {\n\n    @Cacheable(key = \"#id\")\n    UserDO selectById(Integer id);\n\n    @CachePut(key = \"#user.id\")\n    default UserDO insert0(UserDO user) {\n        // 插入记录\n        this.insert(user);\n        // 返回用户\n        return user;\n    }\n\n    @CacheEvict(key = \"#id\")\n    int deleteById(Integer id);\n\n}\n"
  },
  {
    "path": "lab-21/lab-21-cache-redis/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/lab-21-cache-demo?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n  # 对应 RedisProperties 类\n  redis:\n    host: 127.0.0.1\n    port: 6379\n    password: # Redis 服务器密码，默认为空。生产中，一定要设置 Redis 密码！\n    database: 0 # Redis 数据库号，默认为 0 。\n    timeout: 0 # Redis 连接超时时间，单位：毫秒。\n    # 对应 RedisProperties.Jedis 内部类\n    jedis:\n      pool:\n        max-active: 8 # 连接池最大连接数，默认为 8 。使用负数表示没有限制。\n        max-idle: 8 # 默认连接数最小空闲的连接数，默认为 8 。使用负数表示没有限制。\n        min-idle: 0 # 默认连接池最小空闲的连接数，默认为 0 。允许设置 0 和 正数。\n        max-wait: -1 # 连接池最大阻塞等待时间，单位：毫秒。默认为 -1 ，表示不限制。\n  # cache 缓存配置内容\n  cache:\n    type: redis\n\n# mybatis-plus 配置内容\nmybatis-plus:\n  configuration:\n    map-underscore-to-camel-case: true # 虽然默认为 true ，但是还是显示去指定下。\n  global-config:\n    db-config:\n      id-type: auto # ID 主键自增\n      logic-delete-value: 1 # 逻辑已删除值(默认为 1)\n      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)\n  mapper-locations: classpath*:mapper/*.xml\n  type-aliases-package: cn.iocoder.springboot.lab21.cache.dataobject\n\n# logging\nlogging:\n  level:\n    # dao 开启 debug 模式 mybatis 输入 sql\n    cn:\n      iocoder:\n        springboot:\n          lab21:\n            cache:\n              mapper: debug\n"
  },
  {
    "path": "lab-21/lab-21-cache-redis/src/main/resources/sql/users.sql",
    "content": "CREATE TABLE `users` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',\n  `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',\n  `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码',\n  `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n  `deleted` bit(1) DEFAULT NULL COMMENT '是否删除。0-未删除；1-删除',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_username` (`username`)\n) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n\nINSERT INTO `users`(`id`, `username`, `password`, `create_time`, `deleted`) VALUES (1, 'yudaoyuanma', 'buzhidao', now(), 0);\n"
  },
  {
    "path": "lab-21/lab-21-cache-redis/src/test/java/cn/iocoder/springboot/lab21/cache/UserMapperTest.java",
    "content": "package cn.iocoder.springboot.lab21.cache;\n\nimport cn.iocoder.springboot.lab21.cache.dataobject.UserDO;\nimport cn.iocoder.springboot.lab21.cache.mapper.UserMapper;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.cache.CacheManager;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.Date;\nimport java.util.UUID;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserMapperTest {\n\n    private static final String CACHE_NAME_USER = \"users\";\n\n    @Autowired\n    private UserMapper userMapper;\n\n    @Autowired\n    private CacheManager cacheManager;\n\n    @Test\n    public void testCacheManager() {\n        System.out.println(cacheManager);\n    }\n\n    @Test\n    public void testSelectById() {\n        // 这里，胖友事先插入一条 id = 1 的记录。\n        Integer id = 1;\n\n        // 查询 id = 1 的记录\n        UserDO user = userMapper.selectById(id);\n        System.out.println(\"user：\" + user);\n        // 判断缓存中，是不是存在\n        Assert.assertNotNull(\"缓存为空\", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class));\n\n        // 查询 id = 1 的记录\n        user = userMapper.selectById(id);\n        System.out.println(\"user：\" + user);\n    }\n\n    @Test\n    public void testInsert() {\n        // 插入记录\n        UserDO user = new UserDO();\n        user.setUsername(UUID.randomUUID().toString()); // 随机账号，因为唯一索引\n        user.setPassword(\"nicai\");\n        user.setCreateTime(new Date());\n        user.setDeleted(0);\n        userMapper.insert0(user);\n\n        // 判断缓存中，是不是存在\n        Assert.assertNotNull(\"缓存为空\", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class));\n    }\n\n    @Test\n    public void testDeleteById() {\n        // 插入记录，为了让缓存里有记录\n        UserDO user = new UserDO();\n        user.setUsername(UUID.randomUUID().toString()); // 随机账号，因为唯一索引\n        user.setPassword(\"nicai\");\n        user.setCreateTime(new Date());\n        user.setDeleted(0);\n        userMapper.insert0(user);\n        // 判断缓存中，是不是存在\n        Assert.assertNotNull(\"缓存为空\", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class));\n\n        // 删除记录，为了让缓存被删除\n        userMapper.deleteById(user.getId());\n        // 判断缓存中，是不是存在\n        Assert.assertNull(\"缓存不为空\", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class));\n    }\n\n}\n"
  },
  {
    "path": "lab-21/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-21</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-21-cache-demo</module>\n        <module>lab-21-cache-ehcache</module>\n        <module>lab-21-cache-redis</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-21/《芋道 Spring Boot 缓存 Cache 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Cache/?github>\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-22-validation-01</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 保证 Spring AOP 相关的依赖包 -->\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-aspects</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/Application.java",
    "content": "package cn.iocoder.springboot.lab22.validation;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.EnableAspectJAutoProxy;\n\n@SpringBootApplication\n@EnableAspectJAutoProxy(exposeProxy = true) // http://www.voidcn.com/article/p-zddcuyii-bpt.html\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/config/ValidationConfiguration.java",
    "content": "package cn.iocoder.springboot.lab22.validation.config;\n\nimport org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;\nimport org.springframework.context.MessageSource;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;\n\nimport javax.validation.Validator;\n\n@Configuration\npublic class ValidationConfiguration {\n\n    /**\n     * 参考 {@link ValidationAutoConfiguration#defaultValidator()} 方法，构建 Validator Bean\n     *\n     * @return Validator 对象\n     */\n    @Bean\n    public Validator validator(MessageSource messageSource)  {\n        // 创建 LocalValidatorFactoryBean 对象\n        LocalValidatorFactoryBean validator = ValidationAutoConfiguration.defaultValidator();\n        // 设置 messageSource 属性，实现 i18 国际化\n        validator.setValidationMessageSource(messageSource);\n        // 返回\n        return validator;\n    }\n\n}\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/constants/GenderEnum.java",
    "content": "package cn.iocoder.springboot.lab22.validation.constants;\n\nimport cn.iocoder.springboot.lab22.validation.core.validator.IntArrayValuable;\n\nimport java.util.Arrays;\n\npublic enum GenderEnum implements IntArrayValuable {\n\n    MALE(1, \"男\"),\n    FEMALE(2, \"女\");\n\n    /**\n     * 值数组\n     */\n    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(GenderEnum::getValue).toArray();\n\n    /**\n     * 性别值\n     */\n    private final Integer value;\n    /**\n     * 性别名\n     */\n    private final String name;\n\n    GenderEnum(Integer value, String name) {\n        this.value = value;\n        this.name = name;\n    }\n\n    public Integer getValue() {\n        return value;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    @Override\n    public int[] array() {\n        return ARRAYS;\n    }\n\n}\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/constants/ServiceExceptionEnum.java",
    "content": "package cn.iocoder.springboot.lab22.validation.constants;\n\n/**\n * 业务异常枚举\n */\npublic enum ServiceExceptionEnum {\n\n    // ========== 系统级别 ==========\n    SUCCESS(0, \"成功\"),\n    SYS_ERROR(2001001000, \"服务端发生异常\"),\n    MISSING_REQUEST_PARAM_ERROR(2001001001, \"参数缺失\"),\n    INVALID_REQUEST_PARAM_ERROR(2001001002, \"请求参数不合法\"),\n\n    // ========== 用户模块 ==========\n    USER_NOT_FOUND(1001002000, \"用户不存在\"),\n\n    // ========== 订单模块 ==========\n\n    // ========== 商品模块 ==========\n    ;\n\n    /**\n     * 错误码\n     */\n    private final int code;\n    /**\n     * 错误提示\n     */\n    private final String message;\n\n    ServiceExceptionEnum(int code, String message) {\n        this.code = code;\n        this.message = message;\n    }\n\n    public int getCode() {\n        return code;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n}\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab22.validation.controller;\n\nimport cn.iocoder.springboot.lab22.validation.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab22.validation.dto.UserUpdateDTO;\nimport cn.iocoder.springboot.lab22.validation.dto.UserUpdateGenderDTO;\nimport cn.iocoder.springboot.lab22.validation.dto.UserUpdateStatusDTO;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.validation.annotation.Validated;\nimport org.springframework.web.bind.annotation.*;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.Min;\n\n@RestController\n@RequestMapping(\"/users\")\n@Validated\npublic class UserController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/get\")\n    public void get(@RequestParam(\"id\") @Min(value = 1L, message = \"编号必须大于 0\") Integer id) {\n        logger.info(\"[get][id: {}]\", id);\n    }\n\n    @PostMapping(\"/add\")\n    public void add(@Valid UserAddDTO addDTO) {\n        logger.info(\"[add][addDTO: {}]\", addDTO);\n    }\n\n    @PostMapping(\"/update_gender\")\n    public void updateGender(@Valid UserUpdateGenderDTO updateGenderDTO) {\n        logger.info(\"[updateGender][updateGenderDTO: {}]\", updateGenderDTO);\n    }\n\n    @PostMapping(\"/update_status_true\")\n    public void updateStatusTrue(@Validated(UserUpdateStatusDTO.Group01.class) UserUpdateStatusDTO updateStatusDTO) {\n        logger.info(\"[updateStatusTrue][updateStatusDTO: {}]\", updateStatusDTO);\n    }\n\n    @PostMapping(\"/update_status_false\")\n    public void updateStatusFalse(@Validated(UserUpdateStatusDTO.Group02.class) UserUpdateStatusDTO updateStatusDTO) {\n        logger.info(\"[updateStatusFalse][updateStatusDTO: {}]\", updateStatusDTO);\n    }\n\n    @PostMapping(\"/update\")\n    public void update(@Valid UserUpdateDTO updateDTO) {\n        logger.info(\"[update][updateDTO: {}]\", updateDTO);\n    }\n\n}\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/core/exception/ServiceException.java",
    "content": "package cn.iocoder.springboot.lab22.validation.core.exception;\n\nimport cn.iocoder.springboot.lab22.validation.constants.ServiceExceptionEnum;\n\n/**\n * 服务异常\n *\n * 参考 https://www.kancloud.cn/onebase/ob/484204 文章\n *\n * 一共 10 位，分成四段\n *\n * 第一段，1 位，类型\n *      1 - 业务级别异常\n *      2 - 系统级别异常\n * 第二段，3 位，系统类型\n *      001 - 用户系统\n *      002 - 商品系统\n *      003 - 订单系统\n *      004 - 支付系统\n *      005 - 优惠劵系统\n *      ... - ...\n * 第三段，3 位，模块\n *      不限制规则。\n *      一般建议，每个系统里面，可能有多个模块，可以再去做分段。以用户系统为例子：\n *          001 - OAuth2 模块\n *          002 - User 模块\n *          003 - MobileCode 模块\n * 第四段，3 位，错误码\n *       不限制规则。\n *       一般建议，每个模块自增。\n */\npublic final class ServiceException extends RuntimeException {\n\n    /**\n     * 错误码\n     */\n    private final Integer code;\n\n    public ServiceException(ServiceExceptionEnum serviceExceptionEnum) {\n        // 使用父类的 message 字段\n        super(serviceExceptionEnum.getMessage());\n        // 设置错误码\n        this.code = serviceExceptionEnum.getCode();\n    }\n\n    public Integer getCode() {\n        return code;\n    }\n\n}\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/core/package-info.java",
    "content": "/**\n * 提供核心封装\n */\npackage cn.iocoder.springboot.lab22.validation.core;\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/core/validator/InEnum.java",
    "content": "package cn.iocoder.springboot.lab22.validation.core.validator;\n\nimport javax.validation.Constraint;\nimport javax.validation.Payload;\nimport java.lang.annotation.*;\n\nimport static java.lang.annotation.ElementType.*;\n\n@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})\n@Retention(RetentionPolicy.RUNTIME)\n@Documented\n@Constraint(validatedBy = InEnumValidator.class)\npublic @interface InEnum {\n\n    /**\n     * @return 实现 IntArrayValuable 接口的\n     */\n    Class<? extends IntArrayValuable> value();\n\n    /**\n     * @return 提示内容\n     */\n    String message() default \"必须在指定范围 {value}\";\n\n    /**\n     * @return 分组\n     */\n    Class<?>[] groups() default {};\n\n    /**\n     * @return Payload 数组\n     */\n    Class<? extends Payload>[] payload() default {};\n\n    /**\n     *  Defines several {@code @InEnum} constraints on the same element.\n     */\n    @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})\n    @Retention(RetentionPolicy.RUNTIME)\n    @Documented\n    @interface List {\n\n        InEnum[] value();\n\n    }\n\n}\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/core/validator/InEnumValidator.java",
    "content": "package cn.iocoder.springboot.lab22.validation.core.validator;\n\nimport javax.validation.ConstraintValidator;\nimport javax.validation.ConstraintValidatorContext;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Set;\nimport java.util.stream.Collectors;\n\npublic class InEnumValidator implements ConstraintValidator<InEnum, Integer> {\n\n    /**\n     * 值数组\n     */\n    private Set<Integer> values;\n\n    @Override\n    public void initialize(InEnum annotation) {\n        IntArrayValuable[] values = annotation.value().getEnumConstants();\n        if (values.length == 0) {\n            this.values = Collections.emptySet();\n        } else {\n            this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toSet());\n        }\n    }\n\n    @Override\n    public boolean isValid(Integer value, ConstraintValidatorContext context) {\n        // 校验通过\n        if (values.contains(value)) {\n            return true;\n        }\n        // 校验不通过，自定义提示语句（因为，注解上的 value 是枚举类，无法获得枚举类的实际值）\n        context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值\n        context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()\n                .replaceAll(\"\\\\{value}\", values.toString())).addConstraintViolation(); // 重新添加错误提示语句\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/core/validator/IntArrayValuable.java",
    "content": "package cn.iocoder.springboot.lab22.validation.core.validator;\n\n/**\n * 可生成 Int 数组的接口\n */\npublic interface IntArrayValuable {\n\n    /**\n     * @return int 数组\n     */\n    int[] array();\n\n}\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/core/vo/CommonResult.java",
    "content": "package cn.iocoder.springboot.lab22.validation.core.vo;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport org.springframework.util.Assert;\n\nimport java.io.Serializable;\n\n/**\n * 通用返回结果\n *\n * @param <T> 结果泛型\n */\npublic class CommonResult<T> implements Serializable {\n\n    public static Integer CODE_SUCCESS = 0;\n\n    /**\n     * 错误码\n     */\n    private Integer code;\n    /**\n     * 错误提示\n     */\n    private String message;\n    /**\n     * 返回数据\n     */\n    private T data;\n\n    /**\n     * 将传入的 result 对象，转换成另外一个泛型结果的对象\n     *\n     * 因为 A 方法返回的 CommonResult 对象，不满足调用其的 B 方法的返回，所以需要进行转换。\n     *\n     * @param result 传入的 result 对象\n     * @param <T> 返回的泛型\n     * @return 新的 CommonResult 对象\n     */\n    public static <T> CommonResult<T> error(CommonResult<?> result) {\n        return error(result.getCode(), result.getMessage());\n    }\n\n    public static <T> CommonResult<T> error(Integer code, String message) {\n        Assert.isTrue(!CODE_SUCCESS.equals(code), \"code 必须是错误的！\");\n        CommonResult<T> result = new CommonResult<>();\n        result.code = code;\n        result.message = message;\n        return result;\n    }\n\n    public static <T> CommonResult<T> success(T data) {\n        CommonResult<T> result = new CommonResult<>();\n        result.code = CODE_SUCCESS;\n        result.data = data;\n        result.message = \"\";\n        return result;\n    }\n\n    public Integer getCode() {\n        return code;\n    }\n\n    public void setCode(Integer code) {\n        this.code = code;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public void setMessage(String message) {\n        this.message = message;\n    }\n\n    public T getData() {\n        return data;\n    }\n\n    public void setData(T data) {\n        this.data = data;\n    }\n\n    @JsonIgnore\n    public boolean isSuccess() {\n        return CODE_SUCCESS.equals(code);\n    }\n\n    @JsonIgnore\n    public boolean isError() {\n        return !isSuccess();\n    }\n\n    @Override\n    public String toString() {\n        return \"CommonResult{\" +\n                \"code=\" + code +\n                \", message='\" + message + '\\'' +\n                \", data=\" + data +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/core/web/GlobalExceptionHandler.java",
    "content": "package cn.iocoder.springboot.lab22.validation.core.web;\n\nimport cn.iocoder.springboot.lab22.validation.constants.ServiceExceptionEnum;\nimport cn.iocoder.springboot.lab22.validation.core.exception.ServiceException;\nimport cn.iocoder.springboot.lab22.validation.core.vo.CommonResult;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.validation.BindException;\nimport org.springframework.validation.ObjectError;\nimport org.springframework.web.bind.MissingServletRequestParameterException;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.validation.ConstraintViolation;\nimport javax.validation.ConstraintViolationException;\n\n@ControllerAdvice(basePackages = \"cn.iocoder.springboot.lab22.validation.controller\")\npublic class GlobalExceptionHandler {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    /**\n     * 处理 ServiceException 异常\n     */\n    @ResponseBody\n    @ExceptionHandler(value = ServiceException.class)\n    public CommonResult serviceExceptionHandler(HttpServletRequest req, ServiceException ex) {\n        logger.debug(\"[serviceExceptionHandler]\", ex);\n        // 包装 CommonResult 结果\n        return CommonResult.error(ex.getCode(), ex.getMessage());\n    }\n\n    /**\n     * 处理 MissingServletRequestParameterException 异常\n     *\n     * SpringMVC 参数不正确\n     */\n    @ResponseBody\n    @ExceptionHandler(value = MissingServletRequestParameterException.class)\n    public CommonResult missingServletRequestParameterExceptionHandler(HttpServletRequest req, MissingServletRequestParameterException ex) {\n        logger.debug(\"[missingServletRequestParameterExceptionHandler]\", ex);\n        // 包装 CommonResult 结果\n        return CommonResult.error(ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getCode(),\n                ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getMessage());\n    }\n\n    @ResponseBody\n    @ExceptionHandler(value = ConstraintViolationException.class)\n    public CommonResult constraintViolationExceptionHandler(HttpServletRequest req, ConstraintViolationException ex) {\n        logger.debug(\"[constraintViolationExceptionHandler]\", ex);\n        // 拼接错误\n        StringBuilder detailMessage = new StringBuilder();\n        for (ConstraintViolation<?> constraintViolation : ex.getConstraintViolations()) {\n            // 使用 ; 分隔多个错误\n            if (detailMessage.length() > 0) {\n                detailMessage.append(\";\");\n            }\n            // 拼接内容到其中\n            detailMessage.append(constraintViolation.getMessage());\n        }\n        // 包装 CommonResult 结果\n        return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(),\n                ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + \":\" + detailMessage.toString());\n    }\n\n    @ResponseBody\n    @ExceptionHandler(value = BindException.class)\n    public CommonResult bindExceptionHandler(HttpServletRequest req, BindException ex) {\n        logger.debug(\"[bindExceptionHandler]\", ex);\n        // 拼接错误\n        StringBuilder detailMessage = new StringBuilder();\n        for (ObjectError objectError : ex.getAllErrors()) {\n            // 使用 ; 分隔多个错误\n            if (detailMessage.length() > 0) {\n                detailMessage.append(\";\");\n            }\n            // 拼接内容到其中\n            detailMessage.append(objectError.getDefaultMessage());\n        }\n        // 包装 CommonResult 结果\n        return CommonResult.error(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getCode(),\n                ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR.getMessage() + \":\" + detailMessage.toString());\n    }\n\n    /**\n     * 处理其它 Exception 异常\n     */\n    @ResponseBody\n    @ExceptionHandler(value = Exception.class)\n    public CommonResult exceptionHandler(HttpServletRequest req, Exception e) {\n        // 记录异常日志\n        logger.error(\"[exceptionHandler]\", e);\n        // 返回 ERROR CommonResult\n        return CommonResult.error(ServiceExceptionEnum.SYS_ERROR.getCode(),\n                ServiceExceptionEnum.SYS_ERROR.getMessage());\n    }\n\n}\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/core/web/GlobalResponseBodyHandler.java",
    "content": "package cn.iocoder.springboot.lab22.validation.core.web;\n\nimport cn.iocoder.springboot.lab22.validation.core.vo.CommonResult;\nimport org.springframework.core.MethodParameter;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.server.ServerHttpRequest;\nimport org.springframework.http.server.ServerHttpResponse;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;\n\n// 只拦截我们的 Controller 所在包，避免其它类似 swagger 提供的 API 被切面拦截\n@ControllerAdvice(basePackages = \"cn.iocoder.springboot.lab22.validation.controller\")\npublic class GlobalResponseBodyHandler implements ResponseBodyAdvice {\n\n    @Override\n    public boolean supports(MethodParameter returnType, Class converterType) {\n        return true;\n    }\n\n    @Override\n    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType,\n                                  ServerHttpRequest request, ServerHttpResponse response) {\n        // 如果已经是 CommonResult 类型，则直接返回\n        if (body instanceof CommonResult) {\n            return body;\n        }\n        // 如果不是，则包装成 CommonResult 类型\n        return CommonResult.success(body);\n    }\n\n}\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springboot.lab22.validation.dto;\n\nimport org.hibernate.validator.constraints.Length;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.Pattern;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO {\n\n    /**\n     * 账号\n     */\n    @NotEmpty(message = \"登陆账号不能为空\")\n    @Length(min = 5, max = 16, message = \"账号长度为 5-16 位\")\n    @Pattern(regexp = \"^[A-Za-z0-9]+$\", message = \"账号格式为数字以及字母\")\n    private String username;\n    /**\n     * 密码\n     */\n    @NotEmpty(message = \"密码不能为空\")\n    @Length(min = 4, max = 16, message = \"密码长度为 4-16 位\")\n    private String password;\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserAddDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserAddDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserAddDTO{\" +\n                \"username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/dto/UserUpdateDTO.java",
    "content": "package cn.iocoder.springboot.lab22.validation.dto;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * 用户更新 DTO\n */\npublic class UserUpdateDTO {\n\n    /**\n     * 用户编号\n     */\n    @NotNull(message = \"{UserUpdateDTO.id.NotNull}\")\n    private Integer id;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserUpdateDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/dto/UserUpdateGenderDTO.java",
    "content": "package cn.iocoder.springboot.lab22.validation.dto;\n\nimport cn.iocoder.springboot.lab22.validation.constants.GenderEnum;\nimport cn.iocoder.springboot.lab22.validation.core.validator.InEnum;\n\nimport javax.validation.constraints.NotNull;\n\n/**\n * 用户更新性别 DTO\n */\npublic class UserUpdateGenderDTO {\n\n    /**\n     * 用户编号\n     */\n    @NotNull(message = \"用户编号不能为空\")\n    private Integer id;\n\n    /**\n     * 性别\n     */\n    @NotNull(message = \"性别不能为空\")\n    @InEnum(value = GenderEnum.class, message = \"性别必须是 {value}\")\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserUpdateGenderDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserUpdateGenderDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserUpdateGenderDTO{\" +\n                \"id=\" + id +\n                \", gender=\" + gender +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/dto/UserUpdateStatusDTO.java",
    "content": "package cn.iocoder.springboot.lab22.validation.dto;\n\nimport javax.validation.constraints.AssertFalse;\nimport javax.validation.constraints.AssertTrue;\n\n/**\n * 用户更新状态 DTO\n */\npublic class UserUpdateStatusDTO {\n\n    /**\n     * 分组 01 ，要求状态必须为 true\n     */\n    public interface Group01 {}\n\n    /**\n     * 状态 02 ，要求状态必须为 false\n     */\n    public interface Group02 {}\n\n    /**\n     * 状态\n     */\n    @AssertTrue(message = \"状态必须为 true\", groups = Group01.class)\n    @AssertFalse(message = \"状态必须为 false\", groups = Group02.class)\n    private Boolean status;\n\n    public Boolean getStatus() {\n        return status;\n    }\n\n    public UserUpdateStatusDTO setStatus(Boolean status) {\n        this.status = status;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/java/cn/iocoder/springboot/lab22/validation/service/UserService.java",
    "content": "package cn.iocoder.springboot.lab22.validation.service;\n\nimport cn.iocoder.springboot.lab22.validation.dto.UserAddDTO;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.aop.framework.AopContext;\nimport org.springframework.stereotype.Service;\nimport org.springframework.validation.annotation.Validated;\n\nimport javax.validation.Valid;\nimport javax.validation.constraints.Min;\n\n@Service\n@Validated\npublic class UserService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    public void get(@Min(value = 1L, message = \"编号必须大于 0\") Integer id) {\n        logger.info(\"[get][id: {}]\", id);\n    }\n\n    public void add(@Valid UserAddDTO addDTO) {\n        logger.info(\"[add][addDTO: {}]\", addDTO);\n    }\n\n    public void add01(UserAddDTO addDTO) {\n        this.add(addDTO);\n    }\n\n    public void add02(UserAddDTO addDTO) {\n        self().add(addDTO);\n    }\n\n    private UserService self() {\n        return (UserService) AopContext.currentProxy();\n    }\n\n}\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/resources/application.yaml",
    "content": "spring:\n  # i18 message 配置，对应 MessageSourceProperties 配置类\n  messages:\n    basename: i18n/messages # 文件路径基础名\n    encoding: UTF-8 # 使用 UTF-8 编码\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/resources/i18n/messages.properties",
    "content": "UserUpdateDTO.id.NotNull=用户编号不能为空\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/resources/i18n/messages_en.properties",
    "content": "UserUpdateDTO.id.NotNull=userId cannot be empty\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/main/resources/i18n/messages_ja.properties",
    "content": "UserUpdateDTO.id.NotNull=ユーザー番号は空にできません\n"
  },
  {
    "path": "lab-22/lab-22-validation-01/src/test/java/cn/iocoder/springboot/lab22/validation/service/UserServiceTest.java",
    "content": "package cn.iocoder.springboot.lab22.validation.service;\n\nimport cn.iocoder.springboot.lab22.validation.Application;\nimport cn.iocoder.springboot.lab22.validation.dto.UserAddDTO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport javax.validation.ConstraintViolation;\nimport javax.validation.Validator;\nimport java.util.Set;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class UserServiceTest {\n\n    @Autowired\n    private UserService userService;\n\n    @Autowired\n    private Validator validator;\n\n    @Test\n    public void testGet() {\n        userService.get(-1);\n    }\n\n    @Test\n    public void testAdd() {\n        UserAddDTO addDTO = new UserAddDTO();\n        userService.add(addDTO);\n    }\n\n    @Test\n    public void testAdd01() {\n        UserAddDTO addDTO = new UserAddDTO();\n        userService.add01(addDTO);\n    }\n\n    @Test\n    public void testAdd02() {\n        UserAddDTO addDTO = new UserAddDTO();\n        userService.add02(addDTO);\n    }\n\n    @Test\n    public void testValidator() {\n        // 打印，查看 validator 的类型\n        System.out.println(validator);\n\n        // 创建 UserAddDTO 对象\n        UserAddDTO addDTO = new UserAddDTO();\n        // 校验\n        Set<ConstraintViolation<UserAddDTO>> result = validator.validate(addDTO);\n        // 打印校验结果\n        for (ConstraintViolation<UserAddDTO> constraintViolation : result) {\n            // 属性:消息\n            System.out.println(constraintViolation.getPropertyPath() + \":\" + constraintViolation.getMessage());\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-22/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-22</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-22-validation-01</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-22/《芋道 Spring Boot 参数校验 Validation 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Validation/?github>\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-springmvc-23-01</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-01/src/main/java/cn/iocoder/springboot/lab23/springmvc/Application.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-01/src/main/java/cn/iocoder/springboot/lab23/springmvc/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.controller;\n\nimport cn.iocoder.springboot.lab23.springmvc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab23.springmvc.dto.UserUpdateDTO;\nimport cn.iocoder.springboot.lab23.springmvc.service.UserService;\nimport cn.iocoder.springboot.lab23.springmvc.vo.UserVO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 用户 Controller\n */\n@RestController\n@RequestMapping(\"/users\")\npublic class UserController {\n\n    @Autowired\n    private UserService userService;\n\n    /**\n     * 查询用户列表\n     *\n     * @return 用户列表\n     */\n    @GetMapping(\"\")\n    public List<UserVO> list() {\n        // 查询列表\n        List<UserVO> result = new ArrayList<>();\n        result.add(new UserVO().setId(1).setUsername(\"yudaoyuanma\"));\n        result.add(new UserVO().setId(2).setUsername(\"woshiyutou\"));\n        result.add(new UserVO().setId(3).setUsername(\"chifanshuijiao\"));\n        // 返回列表\n        return result;\n    }\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @GetMapping(\"/{id}\")\n    public UserVO get(@PathVariable(\"id\") Integer id) {\n        // 查询并返回用户\n        return new UserVO().setId(id).setUsername(\"username:\" + id);\n    }\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @GetMapping(\"/v2/{id}\")\n    public UserVO get2(@PathVariable(\"id\") Integer id) {\n        return userService.get(id);\n    }\n\n    /**\n     * 添加用户\n     *\n     * @param addDTO 添加用户信息 DTO\n     * @return 添加成功的用户编号\n     */\n    @PostMapping(\"\")\n    public Integer add(UserAddDTO addDTO) {\n        // 插入用户记录，返回编号\n        Integer returnId = 1;\n        // 返回用户编号\n        return returnId;\n    }\n\n    /**\n     * 更新指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @param updateDTO 更新用户信息 DTO\n     * @return 是否修改成功\n     */\n    @PutMapping(\"/{id}\")\n    public Boolean update(@PathVariable(\"id\") Integer id, UserUpdateDTO updateDTO) {\n        // 将 id 设置到 updateDTO 中\n        updateDTO.setId(id);\n        // 更新用户记录\n        Boolean success = true;\n        // 返回更新是否成功\n        return success;\n    }\n\n    /**\n     * 删除指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 是否删除成功\n     */\n    @DeleteMapping(\"/{id}\")\n    public Boolean delete(@PathVariable(\"id\") Integer id) {\n        // 删除用户记录\n        Boolean success = false;\n        // 返回是否更新成功\n        return success;\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-01/src/main/java/cn/iocoder/springboot/lab23/springmvc/controller/UserController2.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.controller;\n\nimport cn.iocoder.springboot.lab23.springmvc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab23.springmvc.dto.UserUpdateDTO;\nimport cn.iocoder.springboot.lab23.springmvc.vo.UserVO;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\n/**\n * 用户2 Controller\n */\n@RestController\n@RequestMapping(\"/users2\")\npublic class UserController2 {\n\n    /**\n     * 查询用户列表\n     *\n     * @return 用户列表\n     */\n    @GetMapping(\"/list\") // URL 修改成 /list\n    public List<UserVO> list() {\n        // 查询列表\n        List<UserVO> result = new ArrayList<>();\n        result.add(new UserVO().setId(1).setUsername(\"yudaoyuanma\"));\n        result.add(new UserVO().setId(2).setUsername(\"woshiyutou\"));\n        result.add(new UserVO().setId(3).setUsername(\"chifanshuijiao\"));\n        // 返回列表\n        return result;\n    }\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @GetMapping(\"/get\") // URL 修改成 /get\n    public UserVO get(@RequestParam(\"id\") Integer id) {\n        // 查询并返回用户\n        return new UserVO().setId(id).setUsername(UUID.randomUUID().toString());\n    }\n\n    /**\n     * 添加用户\n     *\n     * @param addDTO 添加用户信息 DTO\n     * @return 添加成功的用户编号\n     */\n    @PostMapping(\"add\") // URL 修改成 /add\n    public Integer add(UserAddDTO addDTO) {\n        // 插入用户记录，返回编号\n        Integer returnId = UUID.randomUUID().hashCode();\n        // 返回用户编号\n        return returnId;\n    }\n\n    /**\n     * 更新指定用户编号的用户\n     *\n     * @param updateDTO 更新用户信息 DTO\n     * @return 是否修改成功\n     */\n    @PostMapping(\"/update\") // URL 修改成 /update ，RequestMethod 改成 POST\n    public Boolean update(UserUpdateDTO updateDTO) {\n        // 更新用户记录\n        Boolean success = true;\n        // 返回更新是否成功\n        return success;\n    }\n\n    /**\n     * 删除指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 是否删除成功\n     */\n    @DeleteMapping(\"/delete\") // URL 修改成 /delete ，RequestMethod 改成 DELETE\n    public Boolean delete(@RequestParam(\"id\") Integer id) {\n        // 删除用户记录\n        Boolean success = false;\n        // 返回是否更新成功\n        return success;\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-01/src/main/java/cn/iocoder/springboot/lab23/springmvc/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.dto;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO {\n\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserAddDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserAddDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-01/src/main/java/cn/iocoder/springboot/lab23/springmvc/dto/UserUpdateDTO.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.dto;\n\npublic class UserUpdateDTO {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserUpdateDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserUpdateDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserUpdateDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-01/src/main/java/cn/iocoder/springboot/lab23/springmvc/service/UserService.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.service;\n\nimport cn.iocoder.springboot.lab23.springmvc.vo.UserVO;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class UserService {\n\n    public UserVO get(Integer id) {\n        return new UserVO().setId(id).setUsername(\"test\");\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-01/src/main/java/cn/iocoder/springboot/lab23/springmvc/vo/UserVO.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.vo;\n\n/**\n * 用户 VO\n */\npublic class UserVO {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserVO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserVO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-01/src/test/java/cn/iocoder/springboot/lab23/springmvc/controller/UserControllerTest.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.controller;\n\nimport cn.iocoder.springboot.lab23.springmvc.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.MvcResult;\nimport org.springframework.test.web.servlet.ResultActions;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\nimport org.springframework.test.web.servlet.result.MockMvcResultHandlers;\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers;\n\n/**\n * UserController 集成测试\n */\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\n@AutoConfigureMockMvc\npublic class UserControllerTest {\n\n    @Autowired\n    private MockMvc mvc;\n\n    @Test\n    public void testList() throws Exception {\n        // 查询用户列表\n        ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.get(\"/users\"));\n        // 校验结果\n        resultActions.andExpect(MockMvcResultMatchers.status().isOk()); // 响应状态码 200\n        resultActions.andExpect(MockMvcResultMatchers.content().json(\"[\\n\" +\n                \"    {\\n\" +\n                \"        \\\"id\\\": 1,\\n\" +\n                \"        \\\"username\\\": \\\"yudaoyuanma\\\"\\n\" +\n                \"    },\\n\" +\n                \"    {\\n\" +\n                \"        \\\"id\\\": 2,\\n\" +\n                \"        \\\"username\\\": \\\"woshiyutou\\\"\\n\" +\n                \"    },\\n\" +\n                \"    {\\n\" +\n                \"        \\\"id\\\": 3,\\n\" +\n                \"        \\\"username\\\": \\\"chifanshuijiao\\\"\\n\" +\n                \"    }\\n\" +\n                \"]\")); // 响应结果\n    }\n\n    @Test\n    public void testGet() throws Exception {\n        // 获得指定用户编号的用户\n        ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.get(\"/users/1\"));\n        // 校验结果\n        resultActions.andExpect(MockMvcResultMatchers.status().isOk()); // 响应状态码 200\n        resultActions.andExpect(MockMvcResultMatchers.content().json(\"{\\n\" +\n                \"\\\"id\\\": 1,\\n\" +\n                \"\\\"username\\\": \\\"username:1\\\"\\n\" +\n                \"}\")); // 响应结果\n    }\n\n    @Test\n    public void testGet2() throws Exception {\n        // 获得指定用户编号的用户\n        ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.get(\"/users/1\"));\n        // 校验结果\n        resultActions.andExpect(MockMvcResultMatchers.status().isOk()); // 响应状态码 200\n        resultActions.andExpect(MockMvcResultMatchers.content().json(\"{\\n\" +\n                \"\\\"id\\\": 1,\\n\" +\n                \"\\\"username\\\": \\\"username:1\\\"\\n\" +\n                \"}\")); // 响应结果\n\n        // 打印结果\n        resultActions.andDo(MockMvcResultHandlers.print());\n\n        // 获得 MvcResult ，后续执行各种自定义逻辑\n        MvcResult mvcResult = resultActions.andReturn();\n        System.out.println(\"拦截器数量：\" + mvcResult.getInterceptors().length);\n    }\n\n    @Test\n    public void testAdd() throws Exception {\n        // 获得指定用户编号的用户\n        ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.post(\"/users\")\n            .param(\"username\", \"yudaoyuanma\")\n            .param(\"passowrd\", \"nicai\"));\n        // 校验结果\n        resultActions.andExpect(MockMvcResultMatchers.status().isOk()); // 响应状态码 200\n        resultActions.andExpect(MockMvcResultMatchers.content().string(\"1\")); // 响应结果\n    }\n\n    @Test\n    public void testUpdate() throws Exception {\n        // 获得指定用户编号的用户\n        ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.put(\"/users/1\")\n                .param(\"username\", \"yudaoyuanma\")\n                .param(\"passowrd\", \"nicai\"));\n        // 校验结果\n        resultActions.andExpect(MockMvcResultMatchers.status().isOk()); // 响应状态码 200\n        resultActions.andExpect(MockMvcResultMatchers.content().string(\"true\")); // 响应结果\n    }\n\n    @Test\n    public void testDelete() throws Exception {\n        // 获得指定用户编号的用户\n        ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.delete(\"/users/1\"));\n        // 校验结果\n        resultActions.andExpect(MockMvcResultMatchers.status().isOk()); // 响应状态码 200\n        resultActions.andExpect(MockMvcResultMatchers.content().string(\"false\")); // 响应结果\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-01/src/test/java/cn/iocoder/springboot/lab23/springmvc/controller/UserControllerTest2.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.controller;\n\nimport cn.iocoder.springboot.lab23.springmvc.service.UserService;\nimport cn.iocoder.springboot.lab23.springmvc.vo.UserVO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mockito;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;\nimport org.springframework.boot.test.mock.mockito.MockBean;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.ResultActions;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers;\n\n/**\n * UserController 单元测试\n *\n * 参考 https://spring.io/guides/gs/testing-web/ 文章\n */\n@RunWith(SpringRunner.class)\n@WebMvcTest(UserController.class)\npublic class UserControllerTest2 {\n\n    @Autowired\n    private MockMvc mvc;\n\n    @MockBean\n    private UserService userService;\n\n    @Test\n    public void testGet2() throws Exception {\n        // Mock UserService 的 get 方法\n        System.out.println(\"before mock:\" + userService.get(1));\n        Mockito.when(userService.get(1)).thenReturn(\n                new UserVO().setId(1).setUsername(\"username:1\"));\n        System.out.println(\"after mock:\" + userService.get(1));\n\n        // 查询用户列表\n        ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.get(\"/users/v2/1\"));\n        // 校验结果\n        resultActions.andExpect(MockMvcResultMatchers.status().isOk()); // 响应状态码 200\n        resultActions.andExpect(MockMvcResultMatchers.content().json(\"{\\n\" +\n                \"    \\\"id\\\": 1,\\n\" +\n                \"    \\\"username\\\": \\\"username:1\\\"\\n\" +\n                \"}\")); // 响应结果\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-01/src/test/java/cn/iocoder/springboot/lab23/springmvc/package-info.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc;\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-springmvc-23-02</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <!-- 引入 jackson 对 xml 的转换器，实现对 XML 的序列化 -->\n        <dependency>\n            <groupId>com.fasterxml.jackson.dataformat</groupId>\n            <artifactId>jackson-dataformat-xml</artifactId>\n        </dependency>\n\n        <!-- 引入 Fastjson ，实现对 JSON 的序列化 -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n            <version>1.2.62</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/Application.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.web.servlet.ServletComponentScan;\n\n@SpringBootApplication\n@ServletComponentScan\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/config/SpringMVCConfiguration.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.config;\n\nimport cn.iocoder.springboot.lab23.springmvc.core.interceptor.FirstInterceptor;\nimport cn.iocoder.springboot.lab23.springmvc.core.interceptor.SecondInterceptor;\nimport cn.iocoder.springboot.lab23.springmvc.core.interceptor.ThirdInterceptor;\nimport com.alibaba.fastjson.serializer.SerializerFeature;\nimport com.alibaba.fastjson.support.config.FastJsonConfig;\nimport com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.boot.web.servlet.ServletListenerRegistrationBean;\nimport org.springframework.boot.web.servlet.ServletRegistrationBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.converter.HttpMessageConverter;\nimport org.springframework.web.cors.CorsConfiguration;\nimport org.springframework.web.cors.UrlBasedCorsConfigurationSource;\nimport org.springframework.web.filter.CorsFilter;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\nimport javax.servlet.*;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.nio.charset.Charset;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\n@Configuration\npublic class SpringMVCConfiguration implements WebMvcConfigurer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Bean\n    public FirstInterceptor firstInterceptor() {\n        return new FirstInterceptor();\n    }\n\n    @Bean\n    public SecondInterceptor secondInterceptor() {\n        return new SecondInterceptor();\n    }\n\n    @Bean\n    public ThirdInterceptor thirdInterceptor() {\n        return new ThirdInterceptor();\n    }\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        // 拦截器一\n        registry.addInterceptor(this.firstInterceptor()).addPathPatterns(\"/**\");\n        // 拦截器二\n        registry.addInterceptor(this.secondInterceptor()).addPathPatterns(\"/users/current_user\");\n        // 拦截器三\n        registry.addInterceptor(this.thirdInterceptor()).addPathPatterns(\"/**\");\n    }\n\n    @Bean\n    public ServletRegistrationBean testServlet01() {\n        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean<>(new HttpServlet() {\n\n            @Override\n            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n                logger.info(\"[doGet][uri: {}]\", req.getRequestURI());\n            }\n\n        });\n        servletRegistrationBean.setUrlMappings(Collections.singleton(\"/test/01\"));\n        return servletRegistrationBean;\n    }\n\n    @Bean\n    public FilterRegistrationBean testFilter01() {\n        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(new Filter() {\n\n            @Override\n            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {\n                logger.info(\"[doFilter]\");\n                filterChain.doFilter(servletRequest, servletResponse);\n            }\n\n        });\n        filterRegistrationBean.setUrlPatterns(Collections.singleton(\"/test/*\"));\n        return filterRegistrationBean;\n    }\n\n    @Bean\n    public ServletListenerRegistrationBean<?> testListener01() {\n        return new ServletListenerRegistrationBean<>(new ServletContextListener() {\n\n            @Override\n            public void contextInitialized(ServletContextEvent sce) {\n                logger.info(\"[contextInitialized]\");\n            }\n\n            @Override\n            public void contextDestroyed(ServletContextEvent sce) {\n\n            }\n\n        });\n    }\n\n//    @Override\n//    public void addCorsMappings(CorsRegistry registry) {\n//        // 添加全局的 CORS 配置\n//        registry.addMapping(\"/**\") // 匹配所有 URL ，相当于全局配置\n//                .allowedOrigins(\"*\") // 允许所有请求来源\n//                .allowCredentials(true) // 允许发送 Cookie\n//                .allowedMethods(\"*\") // 允许所有请求 Method\n//                .allowedHeaders(\"*\") // 允许所有请求 Header\n////                .exposedHeaders(\"*\") // 允许所有响应 Header\n//                .maxAge(1800L); // 有效期 1800 秒，2 小时\n//    }\n\n    @Bean\n    public FilterRegistrationBean<CorsFilter> corsFilter() {\n        // 创建 UrlBasedCorsConfigurationSource 配置源，类似 CorsRegistry 注册表\n        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n        // 创建 CorsConfiguration 配置，相当于 CorsRegistration 注册信息\n        CorsConfiguration config = new CorsConfiguration();\n        config.setAllowedOrigins(Collections.singletonList(\"*\")); // 允许所有请求来源\n        config.setAllowCredentials(true); // 允许发送 Cookie\n        config.addAllowedMethod(\"*\"); // 允许所有请求 Method\n        config.setAllowedHeaders(Collections.singletonList(\"*\")); // 允许所有请求 Header\n//        config.setExposedHeaders(Collections.singletonList(\"*\")); // 允许所有响应 Header\n        config.setMaxAge(1800L); // 有效期 1800 秒，2 小时\n        source.registerCorsConfiguration(\"/**\", config);\n        // 创建 FilterRegistrationBean 对象\n        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(\n                new CorsFilter(source)); // 创建 CorsFilter 过滤器\n        bean.setOrder(0); // 设置 order 排序。这个顺序很重要哦，为避免麻烦请设置在最前\n        return bean;\n    }\n\n//    @Override\n//    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {\n//        // 增加 XML 消息转换器\n//        Jackson2ObjectMapperBuilder xmlBuilder = Jackson2ObjectMapperBuilder.xml();\n//        xmlBuilder.indentOutput(true);\n//        converters.add(new MappingJackson2XmlHttpMessageConverter(xmlBuilder.build()));\n//    }\n\n//    @Override\n//    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {\n//        // 增加 XML 消息转换器\n//        Jackson2ObjectMapperBuilder xmlBuilder = Jackson2ObjectMapperBuilder.xml();\n//        xmlBuilder.indentOutput(true);\n//        converters.add(new MappingJackson2XmlHttpMessageConverter(xmlBuilder.build()));\n//    }\n\n    @Override\n    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {\n        // 创建 FastJsonHttpMessageConverter 对象\n        FastJsonHttpMessageConverter fastJsonHttpMessageConverter = new FastJsonHttpMessageConverter();\n        // 自定义 FastJson 配置\n        FastJsonConfig fastJsonConfig = new FastJsonConfig();\n        fastJsonConfig.setCharset(Charset.defaultCharset()); // 设置字符集\n        fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect); // 剔除循环引用\n        fastJsonHttpMessageConverter.setFastJsonConfig(fastJsonConfig);\n        // 设置支持的 MediaType\n        fastJsonHttpMessageConverter.setSupportedMediaTypes(Arrays.asList(MediaType.APPLICATION_JSON,\n                MediaType.APPLICATION_JSON_UTF8));\n        // 添加到 converters 中\n        converters.add(0, fastJsonHttpMessageConverter); // 注意，添加到最开头，放在 MappingJackson2XmlHttpMessageConverter 前面\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/constants/ServiceExceptionEnum.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.constants;\n\n/**\n * 业务异常枚举\n */\npublic enum ServiceExceptionEnum {\n\n    // ========== 系统级别 ==========\n    SUCCESS(0, \"成功\"),\n    SYS_ERROR(2001001000, \"服务端发生异常\"),\n    MISSING_REQUEST_PARAM_ERROR(2001001001, \"参数缺失\"),\n\n    // ========== 用户模块 ==========\n    USER_NOT_FOUND(1001002000, \"用户不存在\"),\n\n    // ========== 订单模块 ==========\n\n    // ========== 商品模块 ==========\n    ;\n\n    /**\n     * 错误码\n     */\n    private final int code;\n    /**\n     * 错误提示\n     */\n    private final String message;\n\n    ServiceExceptionEnum(int code, String message) {\n        this.code = code;\n        this.message = message;\n    }\n\n    public int getCode() {\n        return code;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/controller/ProductController.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.controller;\n\nimport cn.iocoder.springboot.lab23.springmvc.vo.ProductVO;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.*;\n\n/**\n * 产品 Controller\n */\n@Deprecated\n@RestController\n@RequestMapping(\"/products\")\npublic class ProductController {\n\n    @PostMapping(value = \"/add\",\n        consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE},\n        produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE}\n    )\n    public ProductVO add(@RequestBody ProductVO product) {\n        return product;\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.controller;\n\nimport cn.iocoder.springboot.lab23.springmvc.constants.ServiceExceptionEnum;\nimport cn.iocoder.springboot.lab23.springmvc.core.exception.ServiceException;\nimport cn.iocoder.springboot.lab23.springmvc.core.vo.CommonResult;\nimport cn.iocoder.springboot.lab23.springmvc.vo.UserVO;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.UUID;\n\n/**\n * 用户 Controller\n */\n@RestController\n@RequestMapping(\"/users\")\n//@CrossOrigin(value = \"*\")\npublic class UserController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * 提供不使用 CommonResult 包装\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @GetMapping(\"/get\")\n    public UserVO get(@RequestParam(\"id\") Integer id) {\n        // 查询并返回用户\n        return new UserVO().setId(id).setUsername(UUID.randomUUID().toString());\n    }\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * 提供使用 CommonResult 包装\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @GetMapping(\"/get2\")\n    public CommonResult<UserVO> get2(@RequestParam(\"id\") Integer id) {\n        // 查询用户\n        UserVO user = new UserVO().setId(id).setUsername(UUID.randomUUID().toString());\n        // 返回结果\n        return CommonResult.success(user);\n    }\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * 测试个问题\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @PostMapping(\"/get\")\n    public UserVO get3(@RequestParam(\"id\") Integer id) {\n        // 查询并返回用户\n        return new UserVO().setId(id).setUsername(UUID.randomUUID().toString());\n    }\n\n    /**\n     * 测试抛出 NullPointerException 异常\n     */\n    @GetMapping(\"/exception-01\")\n    public UserVO exception01() {\n        throw new NullPointerException(\"没有粗面鱼丸\");\n    }\n\n    /**\n     * 测试抛出 ServiceException 异常\n     */\n    @GetMapping(\"/exception-02\")\n    public UserVO exception02() {\n        throw new ServiceException(ServiceExceptionEnum.USER_NOT_FOUND);\n    }\n\n    @GetMapping(\"/do_something\")\n    public void doSomething() {\n        logger.info(\"[doSomething]\");\n    }\n\n    @GetMapping(\"/current_user\")\n    public UserVO currentUser() {\n        logger.info(\"[currentUser]\");\n        return new UserVO().setId(10).setUsername(UUID.randomUUID().toString());\n    }\n\n    @GetMapping(\"/exception-03\")\n    public void exception03() {\n        logger.info(\"[exception03]\");\n        throw new ServiceException(ServiceExceptionEnum.USER_NOT_FOUND);\n    }\n\n    @PostMapping(value = \"/add\",\n            // ↓ 增加 \"application/xml\"、\"application/json\" ，针对 Content-Type 请求头\n            consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE},\n            // ↓ 增加 \"application/xml\"、\"application/json\" ，针对 Accept 请求头\n            produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE}\n    )\n    public UserVO add(@RequestBody UserVO user) {\n        return user;\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/controller2/TestController.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.controller2;\n\nimport cn.iocoder.springboot.lab23.springmvc.core.web.GlobalResponseBodyHandler;\nimport cn.iocoder.springboot.lab23.springmvc.vo.UserVO;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.UUID;\n\n/**\n * 测试 Controller\n *\n * 这个类的目的，主要是为了测试 {@link GlobalResponseBodyHandler} ，不拦截处理这个包\n */\n@RestController\n@RequestMapping(\"/test\")\n//@CrossOrigin(origins = \"*\", allowCredentials = \"true\") // 允许所有来源，允许发送 Cookie\npublic class TestController {\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * @return 用户\n     */\n    @GetMapping(\"/get\")\n//    @CrossOrigin(allowCredentials = \"false\") // 允许所有来源，不允许发送 Cookie\n    public UserVO get() {\n        return new UserVO().setId(1).setUsername(UUID.randomUUID().toString());\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/exception/ServiceException.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.core.exception;\n\nimport cn.iocoder.springboot.lab23.springmvc.constants.ServiceExceptionEnum;\n\n/**\n * 服务异常\n *\n * 参考 https://www.kancloud.cn/onebase/ob/484204 文章\n *\n * 一共 10 位，分成四段\n *\n * 第一段，1 位，类型\n *      1 - 业务级别异常\n *      2 - 系统级别异常\n * 第二段，3 位，系统类型\n *      001 - 用户系统\n *      002 - 商品系统\n *      003 - 订单系统\n *      004 - 支付系统\n *      005 - 优惠劵系统\n *      ... - ...\n * 第三段，3 位，模块\n *      不限制规则。\n *      一般建议，每个系统里面，可能有多个模块，可以再去做分段。以用户系统为例子：\n *          001 - OAuth2 模块\n *          002 - User 模块\n *          003 - MobileCode 模块\n * 第四段，3 位，错误码\n *       不限制规则。\n *       一般建议，每个模块自增。\n */\npublic final class ServiceException extends RuntimeException {\n\n    /**\n     * 错误码\n     */\n    private final Integer code;\n\n    public ServiceException(ServiceExceptionEnum serviceExceptionEnum) {\n        // 使用父类的 message 字段\n        super(serviceExceptionEnum.getMessage());\n        // 设置错误码\n        this.code = serviceExceptionEnum.getCode();\n    }\n\n    public Integer getCode() {\n        return code;\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/interceptor/FirstInterceptor.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.core.interceptor;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.servlet.HandlerInterceptor;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class FirstInterceptor implements HandlerInterceptor {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {\n        logger.info(\"[preHandle][handler({})]\", handler);\n        return true;\n    }\n\n    @Override\n    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {\n        logger.info(\"[postHandle][handler({})]\", handler);\n    }\n\n    @Override\n    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {\n        logger.info(\"[afterCompletion][handler({})]\", handler, ex);\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/interceptor/SecondInterceptor.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.core.interceptor;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.servlet.HandlerInterceptor;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class SecondInterceptor implements HandlerInterceptor {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {\n        logger.info(\"[preHandle][handler({})]\", handler);\n        return false; // 故意返回 false\n    }\n\n    @Override\n    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {\n        logger.info(\"[postHandle][handler({})]\", handler);\n    }\n\n    @Override\n    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {\n        logger.info(\"[afterCompletion][handler({})]\", handler, ex);\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/interceptor/ThirdInterceptor.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.core.interceptor;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.servlet.HandlerInterceptor;\nimport org.springframework.web.servlet.ModelAndView;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\npublic class ThirdInterceptor implements HandlerInterceptor {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {\n        logger.info(\"[preHandle][handler({})]\", handler);\n        return true;\n    }\n\n    @Override\n    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {\n        logger.info(\"[postHandle][handler({})]\", handler);\n    }\n\n    @Override\n    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {\n        logger.info(\"[afterCompletion][handler({})]\", handler, ex);\n        throw new RuntimeException(\"故意抛个错误\"); // 故意抛出异常\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/package-info.java",
    "content": "/**\n * 提供核心封装\n */\npackage cn.iocoder.springboot.lab23.springmvc.core;\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/servlet/TestFilter02.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.core.servlet;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.servlet.*;\nimport javax.servlet.annotation.WebFilter;\nimport java.io.IOException;\n\n@WebFilter(\"/test/*\")\npublic class TestFilter02 implements Filter {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {\n        logger.info(\"[doFilter]\");\n        filterChain.doFilter(servletRequest, servletResponse);\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/servlet/TestServlet02.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.core.servlet;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.annotation.WebServlet;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@WebServlet(urlPatterns = \"/test/02\")\npublic class TestServlet02 extends HttpServlet {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n        logger.info(\"[doGet][uri: {}]\", req.getRequestURI());\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/servlet/TestServletContextListener02.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.core.servlet;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.servlet.ServletContextEvent;\nimport javax.servlet.ServletContextListener;\nimport javax.servlet.annotation.WebListener;\n\n@WebListener\npublic class TestServletContextListener02 implements ServletContextListener {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void contextInitialized(ServletContextEvent sce) {\n        logger.info(\"[contextInitialized]\");\n    }\n\n    @Override\n    public void contextDestroyed(ServletContextEvent sce) {\n\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/vo/CommonResult.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.core.vo;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport org.springframework.util.Assert;\n\nimport java.io.Serializable;\n\n/**\n * 通用返回结果\n *\n * @param <T> 结果泛型\n */\npublic class CommonResult<T> implements Serializable {\n\n    public static Integer CODE_SUCCESS = 0;\n\n    /**\n     * 错误码\n     */\n    private Integer code;\n    /**\n     * 错误提示\n     */\n    private String message;\n    /**\n     * 返回数据\n     */\n    private T data;\n\n    /**\n     * 将传入的 result 对象，转换成另外一个泛型结果的对象\n     *\n     * 因为 A 方法返回的 CommonResult 对象，不满足调用其的 B 方法的返回，所以需要进行转换。\n     *\n     * @param result 传入的 result 对象\n     * @param <T> 返回的泛型\n     * @return 新的 CommonResult 对象\n     */\n    public static <T> CommonResult<T> error(CommonResult<?> result) {\n        return error(result.getCode(), result.getMessage());\n    }\n\n    public static <T> CommonResult<T> error(Integer code, String message) {\n        Assert.isTrue(!CODE_SUCCESS.equals(code), \"code 必须是错误的！\");\n        CommonResult<T> result = new CommonResult<>();\n        result.code = code;\n        result.message = message;\n        return result;\n    }\n\n    public static <T> CommonResult<T> success(T data) {\n        CommonResult<T> result = new CommonResult<>();\n        result.code = CODE_SUCCESS;\n        result.data = data;\n        result.message = \"\";\n        return result;\n    }\n\n    public Integer getCode() {\n        return code;\n    }\n\n    public void setCode(Integer code) {\n        this.code = code;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public void setMessage(String message) {\n        this.message = message;\n    }\n\n    public T getData() {\n        return data;\n    }\n\n    public void setData(T data) {\n        this.data = data;\n    }\n\n    @JsonIgnore\n    public boolean isSuccess() {\n        return CODE_SUCCESS.equals(code);\n    }\n\n    @JsonIgnore\n    public boolean isError() {\n        return !isSuccess();\n    }\n\n    @Override\n    public String toString() {\n        return \"CommonResult{\" +\n                \"code=\" + code +\n                \", message='\" + message + '\\'' +\n                \", data=\" + data +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/web/GlobalExceptionHandler.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.core.web;\n\nimport cn.iocoder.springboot.lab23.springmvc.constants.ServiceExceptionEnum;\nimport cn.iocoder.springboot.lab23.springmvc.core.exception.ServiceException;\nimport cn.iocoder.springboot.lab23.springmvc.core.vo.CommonResult;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.MissingServletRequestParameterException;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport javax.servlet.http.HttpServletRequest;\n\n@ControllerAdvice(basePackages = \"cn.iocoder.springboot.lab23.springmvc.controller\")\npublic class GlobalExceptionHandler {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    /**\n     * 处理 ServiceException 异常\n     */\n    @ResponseBody\n    @ExceptionHandler(value = ServiceException.class)\n    public CommonResult serviceExceptionHandler(HttpServletRequest req, ServiceException ex) {\n        logger.debug(\"[serviceExceptionHandler]\", ex);\n        // 包装 CommonResult 结果\n        return CommonResult.error(ex.getCode(), ex.getMessage());\n    }\n\n    /**\n     * 处理 MissingServletRequestParameterException 异常\n     *\n     * SpringMVC 参数不正确\n     */\n    @ResponseBody\n    @ExceptionHandler(value = MissingServletRequestParameterException.class)\n    public CommonResult missingServletRequestParameterExceptionHandler(HttpServletRequest req, MissingServletRequestParameterException ex) {\n        logger.debug(\"[missingServletRequestParameterExceptionHandler]\", ex);\n        // 包装 CommonResult 结果\n        return CommonResult.error(ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getCode(),\n                ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getMessage());\n    }\n\n    /**\n     * 处理其它 Exception 异常\n     */\n    @ResponseBody\n    @ExceptionHandler(value = Exception.class)\n    public CommonResult exceptionHandler(HttpServletRequest req, Exception e) {\n        // 记录异常日志\n        logger.error(\"[exceptionHandler]\", e);\n        // 返回 ERROR CommonResult\n        return CommonResult.error(ServiceExceptionEnum.SYS_ERROR.getCode(),\n                ServiceExceptionEnum.SYS_ERROR.getMessage());\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/core/web/GlobalResponseBodyHandler.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.core.web;\n\nimport cn.iocoder.springboot.lab23.springmvc.core.vo.CommonResult;\nimport org.springframework.core.MethodParameter;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.server.ServerHttpRequest;\nimport org.springframework.http.server.ServerHttpResponse;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;\n\n// 只拦截我们的 Controller 所在包，避免其它类似 swagger 提供的 API 被切面拦截\n@ControllerAdvice(basePackages = \"cn.iocoder.springboot.lab23.springmvc.controller\")\npublic class GlobalResponseBodyHandler implements ResponseBodyAdvice {\n\n    @Override\n    public boolean supports(MethodParameter returnType, Class converterType) {\n        return true;\n    }\n\n    @Override\n    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType,\n                                  ServerHttpRequest request, ServerHttpResponse response) {\n        // 如果已经是 CommonResult 类型，则直接返回\n        if (body instanceof CommonResult) {\n            return body;\n        }\n        // 如果不是，则包装成 CommonResult 类型\n        return CommonResult.success(body);\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/vo/ProductVO.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.vo;\n\nimport com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;\n\n/**\n * 产品 VO\n */\npublic class ProductVO {\n\n    /**\n     * 商品编号\n     */\n    @JacksonXmlProperty(localName = \"id\")\n    private Integer id;\n    /**\n     * 商品标题\n     */\n    @JacksonXmlProperty(localName = \"title\")\n    private String title;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public ProductVO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getTitle() {\n        return title;\n    }\n\n    public ProductVO setTitle(String title) {\n        this.title = title;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/main/java/cn/iocoder/springboot/lab23/springmvc/vo/UserVO.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc.vo;\n\n/**\n * 用户 VO\n */\npublic class UserVO {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserVO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserVO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-23/lab-springmvc-23-02/src/test/java/cn/iocoder/springboot/lab23/springmvc/package-info.java",
    "content": "package cn.iocoder.springboot.lab23.springmvc;\n"
  },
  {
    "path": "lab-23/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-23</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-springmvc-23-01</module>\n        <module>lab-springmvc-23-02</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-23/《芋道 Spring Boot SpringMVC 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/SpringMVC/?github>\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-japidocs/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-24-apidoc-japidocs</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 JApiDocs 依赖 -->\n        <dependency>\n            <groupId>io.github.yedaxia</groupId>\n            <artifactId>japidocs</artifactId>\n            <version>1.4.4</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-japidocs/src/main/java/cn/iocoder/springboot/lab24/Application.java",
    "content": "package cn.iocoder.springboot.lab24;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-japidocs/src/main/java/cn/iocoder/springboot/lab24/TestJApiDocs.java",
    "content": "package cn.iocoder.springboot.lab24;\n\nimport io.github.yedaxia.apidocs.Docs;\nimport io.github.yedaxia.apidocs.DocsConfig;\nimport io.github.yedaxia.apidocs.plugin.markdown.MarkdownDocPlugin;\n\npublic class TestJApiDocs {\n\n    public static void main(String[] args) {\n        // 1. 创建生成文档的配置\n        DocsConfig config = new DocsConfig();\n        config.setProjectPath(\"/Users/yunai/Java/SpringBoot-Labs/lab-24/lab-24-apidoc-japidocs\"); // 项目所在目录\n        config.setDocsPath(\"/Users/yunai/Downloads/\"); // 生成 HTML 接口文档的目标目录\n        config.setAutoGenerate(true); // 是否给所有 Controller 生成接口文档\n        config.setProjectName(\"示例项目\"); // 项目名\n        config.setApiVersion(\"V1.0\"); // API 版本号\n        config.addPlugin(new MarkdownDocPlugin()); // 使用 MD 插件，额外生成 MD 格式的接口文档\n        // 2. 执行生成 HTML 接口文档\n        Docs.buildHtmlDocs(config);\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-japidocs/src/main/java/cn/iocoder/springboot/lab24/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab24.controller;\n\nimport cn.iocoder.springboot.lab24.vo.UserCreateReqVO;\nimport cn.iocoder.springboot.lab24.vo.UserListReqVO;\nimport cn.iocoder.springboot.lab24.vo.UserRespVO;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.List;\n\n/**\n * 用户 API\n */\n@RestController\n@RequestMapping(\"/api/user/\")\npublic class UserController {\n\n\n    /**\n     * 获得用户列表\n     *\n     * @param listReqVO 列表筛选条件\n     * @return 用户列表\n     */\n    @GetMapping(\"list\")\n    public List<UserRespVO> list(UserListReqVO listReqVO){\n        return null;\n    }\n\n    /**\n     * 保存用户\n     *\n     * @param createReqVO 创建用户信息\n     * @return 用户编号\n     */\n    @PostMapping(\"save\")\n    public Integer saveUser(@RequestBody UserCreateReqVO createReqVO){\n        return 1;\n    }\n\n    /**\n     * 删除指定编号的用户\n     *\n     * @param id 用户编号\n     * @return 是否成功\n     */\n    @DeleteMapping(\"delete\")\n    public Boolean deleteUser(@RequestParam Long id){\n        return true;\n    }\n\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-japidocs/src/main/java/cn/iocoder/springboot/lab24/vo/UserCreateReqVO.java",
    "content": "package cn.iocoder.springboot.lab24.vo;\n\n/**\n * 用户创建请求 VO\n */\npublic class UserCreateReqVO {\n\n    /**\n     * 昵称\n     */\n    private String nickname;\n    /**\n     * 年龄\n     */\n    private Integer age;\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-japidocs/src/main/java/cn/iocoder/springboot/lab24/vo/UserListReqVO.java",
    "content": "package cn.iocoder.springboot.lab24.vo;\n\n/**\n * 用户列表请求 VO\n */\npublic class UserListReqVO {\n\n    /**\n     * 昵称，模糊匹配\n     */\n    private String nickname;\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-japidocs/src/main/java/cn/iocoder/springboot/lab24/vo/UserRespVO.java",
    "content": "package cn.iocoder.springboot.lab24.vo;\n\n/**\n * 用户响应 VO\n */\npublic class UserRespVO {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String nickname;\n    /**\n     * 年龄\n     */\n    private Integer age;\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-showdoc/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.11.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-24-apidoc-showdoc</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-showdoc/showdoc_api.sh",
    "content": "#! /bin/bash\n#\n# 文档说明： https://www.showdoc.com.cn/page/741656402509783\n#\napi_key=\"60fc53cea6af4758c1686cb22ba20566472255580\" \t\t\t#api_key\napi_token=\"0bbb5f564a9ee66333115b1abb8f8d541979489118\" \t#api_token\nurl=\"https://www.showdoc.com.cn/server/?s=/api/open/fromComments\" #同步到的url。使用www.showdoc.com.cn的不需要修改，使用私有版的请修改\n#\n#\n#\n#\n#\n# 如果第一个参数是目录，则使用参数目录。若无，则使用脚本所在的目录。\nif [[ -z \"$1\" ]] || [[ ! -d \"$1\" ]] ; then #目录判断，如果$1不是目录或者是空，则使用当前目录\n\tcurren_dir=$(dirname $(readlink -f $0))\nelse\n\tcurren_dir=$(cd $1; pwd)\nfi\n#echo \"$curren_dir\"\n# 递归搜索文件\nsearchfile() {\n\n\told_IFS=\"$IFS\"\n\tIFS=$'\\n'            #IFS修改\n\tfor chkfile in $1/*\n\tdo\n\t\tfilesize=`ls -l $chkfile | awk '{ print $5 }'`\n\t\tmaxsize=$((1024*1024*1))  # 1M以下的文本文件才会被扫描\n\t\tif [[ -f \"$chkfile\" ]] &&  [ $filesize -le $maxsize ] && [[ -n $(file $chkfile | grep text) ]] ; then # 只对text文件类型操作\n\t\t\techo \"正在扫描 $chkfile\"\n\t\t\tresult=$(sed -n -e '/\\/\\*\\*/,/\\*\\//p' $chkfile | grep showdoc) # 正则匹配\n\t\tif [ ! -z \"$result\" ] ; then\n\t\t\t\t\ttxt=$(sed -n -e '/\\/\\*\\*/,/\\*\\//p' $chkfile)\n\t\t\t\t\t#echo \"sed -n -e '/\\/\\*\\*/,/\\*\\//p' $chkfile\"\n\t\t\t\t\t#echo $result\n\t\t\t\t\tif  [[ $txt =~ \"@url\" ]] && [[ $txt =~ \"@title\" ]]; then\n\t\t\t\t\t\techo -e \"\\033[32m $chkfile 扫描到内容 , 正在生成文档 \\033[0m \"\n\t\t\t\t\t\ttxt2=${txt//&/_this_and_change_}\n\t\t\t\t\t\t# 通过接口生成文档\ncurl -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8'  \"${url}\" --data-binary @- <<CURL_DATA\nfrom=shell&api_key=${api_key}&api_token=${api_token}&content=${txt2}\nCURL_DATA\n\t\t\t\t\tfi\n\t\t\tfi\n\t\tfi\n\n\t\tif [[ -d $chkfile ]] ; then\n\t\t\tsearchfile $chkfile\n\t\tfi\n\tdone\n\tIFS=\"$old_IFS\"\n}\n\n\n#执行搜索\nsearchfile $curren_dir\n\n\n#\nsys=$(uname)\nif [[ $sys =~ \"MS\"  ]] || [[ $sys =~ \"MINGW\"  ]] || [[ $sys =~ \"win\"  ]] ; then\n\tread -s -n1 -p \"按任意键继续 ... \" # win环境下为照顾用户习惯，停顿一下\nfi\n\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-showdoc/showdoc_db.sh",
    "content": "#!/bin/bash\n#\n#\n#\nhost=\"127.0.0.1\"\t\t\t\t#数据库所在地址。默认是localhost\nport=3306\t\t\t\t\t\t#数据库所在端口。默认是3306\nuser=\"root\"   \t \t\t\t#数据库的用户名\npassword=\"123456\" \t\t\t#密码\ndb=\"ruoyi -vue-pro\" \t\t\t\t\t#要同步的数据库名。要同步多个db可以将本脚本复制多份\napi_key=\"60fc53cea6af4758c1686cb22ba20566472255580\" \t\t\t#api_key\napi_token=\"0bbb5f564a9ee66333115b1abb8f8d541979489118\" \t#api_token\ncat_name=\"数据字典\" \t#可选。如果想把生成的文档都放在项目的子目录下，则这里填写子目录名。\nurl=\"https://www.showdoc.com.cn/server/?s=/api/open/updateDbItem\" #可选。同步到的url。如果是使用www.showdoc.com.cn ，则不需要再改此项。如果是部署私有版版showdoc，请改此项为http://xx.com/server/index.php?s=/api/open/updateDbItem 。其中xx.com为你的部署域名\n#\n#\n#\n#\n#\n#\nexport MYSQL_PWD=${password}\nCOMMAND=\"set names utf8;select TABLE_NAME ,TABLE_COMMENT from tables where TABLE_SCHEMA ='${db}'  \"\ndeclare table_info=`mysql -h${host} -P${port} -u${user}  --show-warnings=false -D information_schema -e \"${COMMAND}\" `\n#echo $table_info\nCOMMAND=\"set names utf8;select TABLE_NAME ,COLUMN_NAME, COLUMN_DEFAULT ,IS_NULLABLE ,COLUMN_TYPE ,COLUMN_COMMENT from COLUMNS where TABLE_SCHEMA ='${db}'  \"\ndeclare table_detail=`mysql -h${host} -P${port} -u${user}  --show-warnings=false -D information_schema -e \"${COMMAND}\" `\n#echo $table_detail\ntable_info2=${table_info//&/_this_and_change_}\ntable_detail2=${table_detail//&/_this_and_change_}\ncurl -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8'  \"${url}\" --data-binary @- <<CURL_DATA\nfrom=shell&table_info=${table_info2}&table_detail=${table_detail2}&api_key=${api_key}&api_token=${api_token}&cat_name=${cat_name}\nCURL_DATA\nexport MYSQL_PWD=\"\"\n\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-showdoc/src/main/java/cn/iocoder/springboot/lab24/apidoc/ShowDocApplication.java",
    "content": "package cn.iocoder.springboot.lab24.apidoc;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ShowDocApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ShowDocApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-showdoc/src/main/java/cn/iocoder/springboot/lab24/apidoc/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab24.apidoc.controller;\n\nimport cn.iocoder.springboot.lab24.apidoc.controller.vo.UserLoginReqVO;\nimport cn.iocoder.springboot.lab24.apidoc.controller.vo.UserLoginRespVO;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\n@RequestMapping(\"/users\")\npublic class UserController {\n\n    @PostMapping(\"/login\")\n    public UserLoginRespVO login(@RequestBody UserLoginReqVO reqVO) {\n        UserLoginRespVO respVO = new UserLoginRespVO();\n        respVO.setUserId(1024);\n        respVO.setUsername(reqVO.getUsername());\n        respVO.setName(\"芋道源码\");\n        return respVO;\n    }\n\n    /**\n     * showdoc\n     * @title 用户登录\n     * @description 用户登录的接口\n     * @url http://127.0.0.1:8080/user/login2\n     * @method POST\n     * @json_param {\"username\": \"yudaoyuanma\", \"password\": \"yunai\"}\n     * @param username 必选 string 用户名\n     * @param password 必选 string 密码\n     * @return { \"userId\": 1024, \"name\": \"芋道源码\", \"username\": \"yudaoyuanma\" }\n     * @return_param userId 必选 number 用户编号\n     * @return_param name 必选 string 用户昵称\n     * @return_param username 必选 string 用户账号\n     * @remark 我就是快乐的备注\n     */\n    @PostMapping(\"/login2\")\n    public UserLoginRespVO login2(@RequestBody UserLoginReqVO reqVO) {\n        UserLoginRespVO respVO = new UserLoginRespVO();\n        respVO.setUserId(1024);\n        respVO.setUsername(reqVO.getUsername());\n        respVO.setName(\"芋道源码\");\n        return respVO;\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-showdoc/src/main/java/cn/iocoder/springboot/lab24/apidoc/controller/vo/UserLoginReqVO.java",
    "content": "package cn.iocoder.springboot.lab24.apidoc.controller.vo;\n\npublic class UserLoginReqVO {\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    private String password;\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserLoginReqVO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserLoginReqVO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-showdoc/src/main/java/cn/iocoder/springboot/lab24/apidoc/controller/vo/UserLoginRespVO.java",
    "content": "package cn.iocoder.springboot.lab24.apidoc.controller.vo;\n\npublic class UserLoginRespVO {\n\n    private Integer userId;\n\n    private String name;\n\n    private String username;\n\n    public Integer getUserId() {\n        return userId;\n    }\n\n    public UserLoginRespVO setUserId(Integer userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserLoginRespVO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserLoginRespVO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-showdoc/swagger.json",
    "content": "{\"swagger\":\"2.0\",\"info\":{\"description\":\"我是一段描述\",\"version\":\"1.0.0\",\"title\":\"测试接口文档示例\",\"contact\":{\"name\":\"芋艿\",\"url\":\"http://www.iocoder.cn\",\"email\":\"zhijiantianya@gmail.com\"}},\"host\":\"127.0.0.1:8080\",\"basePath\":\"/\",\"tags\":[{\"name\":\"用户 API 接口\",\"description\":\"User Controller\"}],\"paths\":{\"/users/add\":{\"post\":{\"tags\":[\"用户 API 接口\"],\"summary\":\"添加用户\",\"operationId\":\"addUsingPOST\",\"consumes\":[\"application/json\"],\"produces\":[\"*/*\"],\"parameters\":[{\"name\":\"password\",\"in\":\"query\",\"description\":\"密码\",\"required\":true,\"type\":\"string\",\"x-example\":\"nicai\"},{\"name\":\"username\",\"in\":\"query\",\"description\":\"账号\",\"required\":true,\"type\":\"string\",\"x-example\":\"yudaoyuanma\"}],\"responses\":{\"200\":{\"description\":\"OK\",\"schema\":{\"type\":\"integer\",\"format\":\"int32\"}},\"201\":{\"description\":\"Created\"},\"401\":{\"description\":\"Unauthorized\"},\"403\":{\"description\":\"Forbidden\"},\"404\":{\"description\":\"Not Found\"}}}},\"/users/delete\":{\"post\":{\"tags\":[\"用户 API 接口\"],\"summary\":\"删除指定用户编号的用户\",\"operationId\":\"deleteUsingPOST\",\"consumes\":[\"application/json\"],\"produces\":[\"*/*\"],\"parameters\":[{\"name\":\"id\",\"in\":\"query\",\"description\":\"用户编号\",\"required\":true,\"type\":\"integer\",\"format\":\"int32\",\"x-example\":1024}],\"responses\":{\"200\":{\"description\":\"OK\",\"schema\":{\"type\":\"boolean\"}},\"201\":{\"description\":\"Created\"},\"401\":{\"description\":\"Unauthorized\"},\"403\":{\"description\":\"Forbidden\"},\"404\":{\"description\":\"Not Found\"}}}},\"/users/get\":{\"get\":{\"tags\":[\"用户 API 接口\"],\"summary\":\"获得指定用户编号的用户\",\"operationId\":\"getUsingGET\",\"produces\":[\"*/*\"],\"parameters\":[{\"name\":\"id\",\"in\":\"query\",\"description\":\"用户编号\",\"required\":true,\"type\":\"integer\",\"format\":\"int32\",\"x-example\":1024}],\"responses\":{\"200\":{\"description\":\"OK\",\"schema\":{\"$ref\":\"#/definitions/用户 VO\"}},\"401\":{\"description\":\"Unauthorized\"},\"403\":{\"description\":\"Forbidden\"},\"404\":{\"description\":\"Not Found\"}}}},\"/users/list\":{\"get\":{\"tags\":[\"用户 API 接口\"],\"summary\":\"查询用户列表\",\"description\":\"目前仅仅是作为测试，所以返回用户全列表\",\"operationId\":\"listUsingGET\",\"produces\":[\"*/*\"],\"responses\":{\"200\":{\"description\":\"OK\",\"schema\":{\"type\":\"array\",\"items\":{\"$ref\":\"#/definitions/用户 VO\"}}},\"401\":{\"description\":\"Unauthorized\"},\"403\":{\"description\":\"Forbidden\"},\"404\":{\"description\":\"Not Found\"}}}},\"/users/update\":{\"post\":{\"tags\":[\"用户 API 接口\"],\"summary\":\"更新指定用户编号的用户\",\"operationId\":\"updateUsingPOST\",\"consumes\":[\"application/json\"],\"produces\":[\"*/*\"],\"parameters\":[{\"name\":\"id\",\"in\":\"query\",\"description\":\"用户编号\",\"required\":true,\"type\":\"integer\",\"format\":\"int32\",\"x-example\":1024},{\"name\":\"password\",\"in\":\"query\",\"description\":\"密码\",\"required\":true,\"type\":\"string\",\"x-example\":\"nicai\"},{\"name\":\"username\",\"in\":\"query\",\"description\":\"账号\",\"required\":true,\"type\":\"string\",\"x-example\":\"yudaoyuanma\"}],\"responses\":{\"200\":{\"description\":\"OK\",\"schema\":{\"type\":\"boolean\"}},\"201\":{\"description\":\"Created\"},\"401\":{\"description\":\"Unauthorized\"},\"403\":{\"description\":\"Forbidden\"},\"404\":{\"description\":\"Not Found\"}}}}},\"definitions\":{\"用户 VO\":{\"type\":\"object\",\"required\":[\"id\",\"username\"],\"properties\":{\"id\":{\"type\":\"integer\",\"format\":\"int32\",\"example\":1024,\"description\":\"用户编号\"},\"username\":{\"type\":\"string\",\"example\":\"yudaoyuanma\",\"description\":\"账号\"}},\"title\":\"用户 VO\"}}}"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-24-apidoc-swagger</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Swagger 依赖 -->\n        <dependency>\n            <groupId>io.springfox</groupId>\n            <artifactId>springfox-swagger2</artifactId>\n            <version>2.9.2</version>\n        </dependency>\n\n        <!-- 引入 Swagger UI 依赖，以实现 API 接口的 UI 界面 -->\n        <dependency>\n            <groupId>io.springfox</groupId>\n            <artifactId>springfox-swagger-ui</artifactId>\n            <version>2.9.2</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger/src/main/java/cn/iocoder/springboot/lab24/apidoc/Application.java",
    "content": "package cn.iocoder.springboot.lab24.apidoc;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger/src/main/java/cn/iocoder/springboot/lab24/apidoc/config/SwaggerConfiguration.java",
    "content": "package cn.iocoder.springboot.lab24.apidoc.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport springfox.documentation.builders.ApiInfoBuilder;\nimport springfox.documentation.builders.PathSelectors;\nimport springfox.documentation.builders.RequestHandlerSelectors;\nimport springfox.documentation.service.ApiInfo;\nimport springfox.documentation.service.Contact;\nimport springfox.documentation.spi.DocumentationType;\nimport springfox.documentation.spring.web.plugins.Docket;\nimport springfox.documentation.swagger2.annotations.EnableSwagger2;\n\n@Configuration\n@EnableSwagger2 // 标记项目启用 Swagger API 接口文档\npublic class SwaggerConfiguration {\n\n    @Bean\n    public Docket createRestApi() {\n        // 创建 Docket 对象\n        return new Docket(DocumentationType.SWAGGER_2) // 文档类型，使用 Swagger2\n                .apiInfo(this.apiInfo()) // 设置 API 信息\n                // 扫描 Controller 包路径，获得 API 接口\n                .select()\n                .apis(RequestHandlerSelectors.basePackage(\"cn.iocoder.springboot.lab24.apidoc.controller\"))\n                .paths(PathSelectors.any())\n                // 构建出 Docket 对象\n                .build();\n    }\n\n    /**\n     * 创建 API 信息\n     */\n    private ApiInfo apiInfo() {\n        return new ApiInfoBuilder()\n                .title(\"测试接口文档示例\")\n                .description(\"我是一段描述\")\n                .version(\"1.0.0\") // 版本号\n                .contact(new Contact(\"芋艿\", \"http://www.iocoder.cn\", \"zhijiantianya@gmail.com\")) // 联系人\n                .build();\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger/src/main/java/cn/iocoder/springboot/lab24/apidoc/controller/TestController.java",
    "content": "package cn.iocoder.springboot.lab24.apidoc.controller;\n\nimport cn.iocoder.springboot.lab24.apidoc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab24.apidoc.dto.UserUpdateDTO;\nimport cn.iocoder.springboot.lab24.apidoc.vo.UserVO;\nimport io.swagger.annotations.ApiImplicitParam;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\n//@RestController\n@RequestMapping(\"/tests\")\n//@Api(tags = \"用户 API 接口\")\npublic class TestController {\n\n    @GetMapping(\"/list\")\n    @ApiOperation(value = \"查询用户列表\", notes = \"目前仅仅是作为测试，所以返回用户全列表\")\n    public List<UserVO> list() {\n        // 查询列表\n        List<UserVO> result = new ArrayList<>();\n        result.add(new UserVO().setId(1).setUsername(\"yudaoyuanma\"));\n        result.add(new UserVO().setId(2).setUsername(\"woshiyutou\"));\n        result.add(new UserVO().setId(3).setUsername(\"chifanshuijiao\"));\n        // 返回列表\n        return result;\n    }\n\n    @GetMapping(\"/get\")\n    @ApiOperation(\"获得指定用户编号的用户\")\n    @ApiImplicitParam(paramType = \"query\", dataType = \"Integer\", name = \"id\", value = \"用户编号\", required = true, example = \"1024\")\n    public UserVO get(@RequestParam(\"id\") Integer id) {\n        // 查询并返回用户\n        return new UserVO().setId(id).setUsername(UUID.randomUUID().toString());\n    }\n\n    @PostMapping(\"add\")\n    @ApiOperation(\"添加用户\")\n    public Integer add(UserAddDTO addDTO) {\n        // 插入用户记录，返回编号\n        Integer returnId = UUID.randomUUID().hashCode();\n        // 返回用户编号\n        return returnId;\n    }\n\n    @PostMapping(\"/update\")\n    @ApiOperation(\"更新指定用户编号的用户\")\n    public Boolean update(UserUpdateDTO updateDTO) {\n        // 更新用户记录\n        Boolean success = true;\n        // 返回更新是否成功\n        return success;\n    }\n\n    @PostMapping(\"/delete\")\n    @ApiOperation(\"删除指定用户编号的用户\")\n    @ApiImplicitParam(paramType = \"query\", dataTypeClass = Integer.class, name = \"id\", value = \"用户编号\", required = true, example = \"1024\")\n    public Boolean delete(@RequestParam(\"id\") Integer id) {\n        // 删除用户记录\n        Boolean success = false;\n        // 返回是否更新成功\n        return success;\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger/src/main/java/cn/iocoder/springboot/lab24/apidoc/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab24.apidoc.controller;\n\nimport cn.iocoder.springboot.lab24.apidoc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab24.apidoc.dto.UserUpdateDTO;\nimport cn.iocoder.springboot.lab24.apidoc.vo.UserVO;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiImplicitParam;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\n@RestController\n@RequestMapping(\"/users\")\n@Api(tags = \"用户 API 接口\")\npublic class UserController {\n\n    @GetMapping(\"/list\")\n    @ApiOperation(value = \"查询用户列表\", notes = \"目前仅仅是作为测试，所以返回用户全列表\")\n    public List<UserVO> list() {\n        // 查询列表\n        List<UserVO> result = new ArrayList<>();\n        result.add(new UserVO().setId(1).setUsername(\"yudaoyuanma\"));\n        result.add(new UserVO().setId(2).setUsername(\"woshiyutou\"));\n        result.add(new UserVO().setId(3).setUsername(\"chifanshuijiao\"));\n        // 返回列表\n        return result;\n    }\n\n    @GetMapping(\"/get\")\n    @ApiOperation(\"获得指定用户编号的用户\")\n    @ApiImplicitParam(name = \"id\", value = \"用户编号\", paramType = \"query\", dataTypeClass = Integer.class, required = true, example = \"1024\")\n    public UserVO get(@RequestParam(\"id\") Integer id) {\n        // 查询并返回用户\n        return new UserVO().setId(id).setUsername(UUID.randomUUID().toString());\n    }\n\n    @PostMapping(\"add\")\n    @ApiOperation(\"添加用户\")\n    public Integer add(UserAddDTO addDTO) {\n        // 插入用户记录，返回编号\n        Integer returnId = UUID.randomUUID().hashCode();\n        // 返回用户编号\n        return returnId;\n    }\n\n    @PostMapping(\"/update\")\n    @ApiOperation(\"更新指定用户编号的用户\")\n    public Boolean update(UserUpdateDTO updateDTO) {\n        // 更新用户记录\n        Boolean success = true;\n        // 返回更新是否成功\n        return success;\n    }\n\n    @PostMapping(\"/delete\")\n    @ApiOperation(value = \"删除指定用户编号的用户\")\n    @ApiImplicitParam(name = \"id\", value = \"用户编号\", paramType = \"query\", dataTypeClass = Integer.class, required = true, example = \"1024\")\n    public Boolean delete(@RequestParam(\"id\") Integer id) {\n        // 删除用户记录\n        Boolean success = false;\n        // 返回是否更新成功\n        return success;\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger/src/main/java/cn/iocoder/springboot/lab24/apidoc/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springboot.lab24.apidoc.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\n\n@ApiModel(\"用户添加 DTO\")\npublic class UserAddDTO {\n\n    @ApiModelProperty(value = \"账号\", required = true, example = \"yudaoyuanma\")\n    private String username;\n    @ApiModelProperty(value = \"密码\", required = true, example = \"nicai\")\n    private String password;\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserAddDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserAddDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger/src/main/java/cn/iocoder/springboot/lab24/apidoc/dto/UserUpdateDTO.java",
    "content": "package cn.iocoder.springboot.lab24.apidoc.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\n\n@ApiModel(\"用户更新 DTO\")\npublic class UserUpdateDTO {\n\n    @ApiModelProperty(value = \"用户编号\", required = true, example = \"1024\")\n    private Integer id;\n    @ApiModelProperty(value = \"账号\", required = true, example = \"yudaoyuanma\")\n    private String username;\n    @ApiModelProperty(value = \"密码\", required = true, example = \"nicai\")\n    private String password;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserUpdateDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserUpdateDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserUpdateDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger/src/main/java/cn/iocoder/springboot/lab24/apidoc/vo/UserVO.java",
    "content": "package cn.iocoder.springboot.lab24.apidoc.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\n\n@ApiModel(\"用户 VO\")\npublic class UserVO {\n\n    @ApiModelProperty(value = \"用户编号\", required = true, example = \"1024\")\n    private Integer id;\n    @ApiModelProperty(value = \"账号\", required = true, example = \"yudaoyuanma\")\n    private String username;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserVO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserVO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger-knife4j/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.3.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-24-apidoc-swagger-knife4j</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 1. swagger-bootstrap-ui 目前改名为 knife4j -->\n        <!-- 2. 实现 swagger-bootstrap-ui 的自动化配置  -->\n        <!-- 3. 因为 knife4j-spring 已经引入 Swagger 依赖，所以无需重复引入 -->\n        <dependency>\n            <groupId>com.github.xiaoymin</groupId>\n            <artifactId>knife4j-spring</artifactId>\n            <version>1.9.6</version>\n        </dependency>\n        <dependency>\n            <groupId>com.github.xiaoymin</groupId>\n            <artifactId>knife4j-spring-ui</artifactId>\n            <version>1.9.6</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger-knife4j/src/main/java/cn/iocoder/springboot/lab24/apidoc/Application.java",
    "content": "package cn.iocoder.springboot.lab24.apidoc;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger-knife4j/src/main/java/cn/iocoder/springboot/lab24/apidoc/config/SwaggerConfiguration.java",
    "content": "package cn.iocoder.springboot.lab24.apidoc.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport springfox.documentation.builders.ApiInfoBuilder;\nimport springfox.documentation.builders.PathSelectors;\nimport springfox.documentation.builders.RequestHandlerSelectors;\nimport springfox.documentation.service.ApiInfo;\nimport springfox.documentation.service.Contact;\nimport springfox.documentation.spi.DocumentationType;\nimport springfox.documentation.spring.web.plugins.Docket;\nimport springfox.documentation.swagger2.annotations.EnableSwagger2;\n\n@Configuration\n@EnableSwagger2 // 标记项目启用 Swagger API 接口文档\npublic class SwaggerConfiguration {\n\n    @Bean\n    public Docket createRestApi() {\n        // 创建 Docket 对象\n        return new Docket(DocumentationType.SWAGGER_2) // 文档类型，使用 Swagger2\n                .apiInfo(this.apiInfo()) // 设置 API 信息\n                // 扫描 Controller 包路径，获得 API 接口\n                .select()\n                .apis(RequestHandlerSelectors.basePackage(\"cn.iocoder.springboot.lab24.apidoc.controller\"))\n                .paths(PathSelectors.any())\n                // 构建出 Docket 对象\n                .build();\n    }\n\n    /**\n     * 创建 API 信息\n     */\n    private ApiInfo apiInfo() {\n        return new ApiInfoBuilder()\n                .title(\"测试接口文档示例\")\n                .description(\"我是一段描述\")\n                .version(\"1.0.0\") // 版本号\n                .contact(new Contact(\"芋艿\", \"http://www.iocoder.cn\", \"zhijiantianya@gmail.com\")) // 联系人\n                .build();\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger-knife4j/src/main/java/cn/iocoder/springboot/lab24/apidoc/controller/TestController.java",
    "content": "package cn.iocoder.springboot.lab24.apidoc.controller;\n\nimport cn.iocoder.springboot.lab24.apidoc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab24.apidoc.dto.UserUpdateDTO;\nimport cn.iocoder.springboot.lab24.apidoc.vo.UserVO;\nimport io.swagger.annotations.ApiImplicitParam;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\n//@RestController\n@RequestMapping(\"/tests\")\n//@Api(tags = \"用户 API 接口\")\npublic class TestController {\n\n    @GetMapping(\"/list\")\n    @ApiOperation(value = \"查询用户列表\", notes = \"目前仅仅是作为测试，所以返回用户全列表\")\n    public List<UserVO> list() {\n        // 查询列表\n        List<UserVO> result = new ArrayList<>();\n        result.add(new UserVO().setId(1).setUsername(\"yudaoyuanma\"));\n        result.add(new UserVO().setId(2).setUsername(\"woshiyutou\"));\n        result.add(new UserVO().setId(3).setUsername(\"chifanshuijiao\"));\n        // 返回列表\n        return result;\n    }\n\n    @GetMapping(\"/get\")\n    @ApiOperation(\"获得指定用户编号的用户\")\n    @ApiImplicitParam(paramType = \"query\", dataType = \"Integer\", name = \"id\", value = \"用户编号\", required = true, example = \"1024\")\n    public UserVO get(@RequestParam(\"id\") Integer id) {\n        // 查询并返回用户\n        return new UserVO().setId(id).setUsername(UUID.randomUUID().toString());\n    }\n\n    @PostMapping(\"add\")\n    @ApiOperation(\"添加用户\")\n    public Integer add(UserAddDTO addDTO) {\n        // 插入用户记录，返回编号\n        Integer returnId = UUID.randomUUID().hashCode();\n        // 返回用户编号\n        return returnId;\n    }\n\n    @PostMapping(\"/update\")\n    @ApiOperation(\"更新指定用户编号的用户\")\n    public Boolean update(UserUpdateDTO updateDTO) {\n        // 更新用户记录\n        Boolean success = true;\n        // 返回更新是否成功\n        return success;\n    }\n\n    @PostMapping(\"/delete\")\n    @ApiOperation(\"删除指定用户编号的用户\")\n    @ApiImplicitParam(paramType = \"query\", dataTypeClass = Integer.class, name = \"id\", value = \"用户编号\", required = true, example = \"1024\")\n    public Boolean delete(@RequestParam(\"id\") Integer id) {\n        // 删除用户记录\n        Boolean success = false;\n        // 返回是否更新成功\n        return success;\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger-knife4j/src/main/java/cn/iocoder/springboot/lab24/apidoc/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab24.apidoc.controller;\n\nimport cn.iocoder.springboot.lab24.apidoc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab24.apidoc.dto.UserUpdateDTO;\nimport cn.iocoder.springboot.lab24.apidoc.vo.UserVO;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiImplicitParam;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\n@RestController\n@RequestMapping(\"/users\")\n@Api(tags = \"用户 API 接口\")\npublic class UserController {\n\n    @GetMapping(\"/list\")\n    @ApiOperation(value = \"查询用户列表\", notes = \"目前仅仅是作为测试，所以返回用户全列表\")\n    public List<UserVO> list() {\n        // 查询列表\n        List<UserVO> result = new ArrayList<>();\n        result.add(new UserVO().setId(1).setUsername(\"yudaoyuanma\"));\n        result.add(new UserVO().setId(2).setUsername(\"woshiyutou\"));\n        result.add(new UserVO().setId(3).setUsername(\"chifanshuijiao\"));\n        // 返回列表\n        return result;\n    }\n\n    @GetMapping(\"/get\")\n    @ApiOperation(\"获得指定用户编号的用户\")\n    @ApiImplicitParam(name = \"id\", value = \"用户编号\", paramType = \"query\", dataTypeClass = Integer.class, required = true, example = \"1024\")\n    public UserVO get(@RequestParam(\"id\") Integer id) {\n        // 查询并返回用户\n        return new UserVO().setId(id).setUsername(UUID.randomUUID().toString());\n    }\n\n    @PostMapping(\"add\")\n    @ApiOperation(\"添加用户\")\n    public Integer add(UserAddDTO addDTO) {\n        // 插入用户记录，返回编号\n        Integer returnId = UUID.randomUUID().hashCode();\n        // 返回用户编号\n        return returnId;\n    }\n\n    @PostMapping(\"/update\")\n    @ApiOperation(\"更新指定用户编号的用户\")\n    public Boolean update(UserUpdateDTO updateDTO) {\n        // 更新用户记录\n        Boolean success = true;\n        // 返回更新是否成功\n        return success;\n    }\n\n    @PostMapping(\"/delete\")\n    @ApiOperation(value = \"删除指定用户编号的用户\")\n    @ApiImplicitParam(name = \"id\", value = \"用户编号\", paramType = \"query\", dataTypeClass = Integer.class, required = true, example = \"1024\")\n    public Boolean delete(@RequestParam(\"id\") Integer id) {\n        // 删除用户记录\n        Boolean success = false;\n        // 返回是否更新成功\n        return success;\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger-knife4j/src/main/java/cn/iocoder/springboot/lab24/apidoc/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springboot.lab24.apidoc.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\n\n@ApiModel(\"用户添加 DTO\")\npublic class UserAddDTO {\n\n    @ApiModelProperty(value = \"账号\", required = true, example = \"yudaoyuanma\")\n    private String username;\n    @ApiModelProperty(value = \"密码\", required = true, example = \"nicai\")\n    private String password;\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserAddDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserAddDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger-knife4j/src/main/java/cn/iocoder/springboot/lab24/apidoc/dto/UserUpdateDTO.java",
    "content": "package cn.iocoder.springboot.lab24.apidoc.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\n\n@ApiModel(\"用户更新 DTO\")\npublic class UserUpdateDTO {\n\n    @ApiModelProperty(value = \"用户编号\", required = true, example = \"1024\")\n    private Integer id;\n    @ApiModelProperty(value = \"账号\", required = true, example = \"yudaoyuanma\")\n    private String username;\n    @ApiModelProperty(value = \"密码\", required = true, example = \"nicai\")\n    private String password;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserUpdateDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserUpdateDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserUpdateDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger-knife4j/src/main/java/cn/iocoder/springboot/lab24/apidoc/vo/UserVO.java",
    "content": "package cn.iocoder.springboot.lab24.apidoc.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\n\n@ApiModel(\"用户 VO\")\npublic class UserVO {\n\n    @ApiModelProperty(value = \"用户编号\", required = true, example = \"1024\")\n    private Integer id;\n    @ApiModelProperty(value = \"账号\", required = true, example = \"yudaoyuanma\")\n    private String username;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserVO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserVO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger-starter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.11.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-24-apidoc-swagger-starter</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Swagger 的自动配置 -->\n        <dependency>\n            <groupId>io.springfox</groupId>\n            <artifactId>springfox-boot-starter</artifactId>\n            <version>3.0.0</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger-starter/src/main/java/cn/iocoder/springboot/lab24/Application.java",
    "content": "package cn.iocoder.springboot.lab24;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger-starter/src/main/java/cn/iocoder/springboot/lab24/config/SwaggerConfiguration.java",
    "content": "package cn.iocoder.springboot.lab24.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport springfox.documentation.builders.ApiInfoBuilder;\nimport springfox.documentation.builders.PathSelectors;\nimport springfox.documentation.builders.RequestHandlerSelectors;\nimport springfox.documentation.service.ApiInfo;\nimport springfox.documentation.service.Contact;\nimport springfox.documentation.spi.DocumentationType;\nimport springfox.documentation.spring.web.plugins.Docket;\n\n@Configuration\n// @EnableSwagger2 无需使用该注解\npublic class SwaggerConfiguration {\n\n    @Bean\n    public Docket createRestApi() {\n        // 创建 Docket 对象\n        return new Docket(DocumentationType.SWAGGER_2) // 文档类型，使用 Swagger2\n                .apiInfo(this.apiInfo()) // 设置 API 信息\n                // 扫描 Controller 包路径，获得 API 接口\n                .select()\n                .apis(RequestHandlerSelectors.basePackage(\"cn.iocoder.springboot.lab24.controller\"))\n                .paths(PathSelectors.any())\n                // 构建出 Docket 对象\n                .build();\n    }\n\n    /**\n     * 创建 API 信息\n     */\n    private ApiInfo apiInfo() {\n        return new ApiInfoBuilder()\n                .title(\"测试接口文档示例\")\n                .description(\"我是一段描述\")\n                .version(\"1.0.0\") // 版本号\n                .contact(new Contact(\"芋艿\", \"http://www.iocoder.cn\", \"zhijiantianya@gmail.com\")) // 联系人\n                .build();\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger-starter/src/main/java/cn/iocoder/springboot/lab24/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab24.controller;\n\nimport cn.iocoder.springboot.lab24.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab24.dto.UserUpdateDTO;\nimport cn.iocoder.springboot.lab24.vo.UserVO;\nimport io.swagger.annotations.Api;\nimport io.swagger.annotations.ApiImplicitParam;\nimport io.swagger.annotations.ApiOperation;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\n@RestController\n@RequestMapping(\"/users\")\n@Api(tags = \"用户 API 接口\")\npublic class UserController {\n\n    @GetMapping(\"/list\")\n    @ApiOperation(value = \"查询用户列表\", notes = \"目前仅仅是作为测试，所以返回用户全列表\")\n    public List<UserVO> list() {\n        // 查询列表\n        List<UserVO> result = new ArrayList<>();\n        result.add(new UserVO().setId(1).setUsername(\"yudaoyuanma\"));\n        result.add(new UserVO().setId(2).setUsername(\"woshiyutou\"));\n        result.add(new UserVO().setId(3).setUsername(\"chifanshuijiao\"));\n        // 返回列表\n        return result;\n    }\n\n    @GetMapping(\"/get\")\n    @ApiOperation(\"获得指定用户编号的用户\")\n    @ApiImplicitParam(name = \"id\", value = \"用户编号\", paramType = \"query\", dataTypeClass = Integer.class, required = true, example = \"1024\")\n    public UserVO get(@RequestParam(\"id\") Integer id) {\n        // 查询并返回用户\n        return new UserVO().setId(id).setUsername(UUID.randomUUID().toString());\n    }\n\n    @PostMapping(\"add\")\n    @ApiOperation(\"添加用户\")\n    public Integer add(UserAddDTO addDTO) {\n        // 插入用户记录，返回编号\n        Integer returnId = UUID.randomUUID().hashCode();\n        // 返回用户编号\n        return returnId;\n    }\n\n    @PostMapping(\"/update\")\n    @ApiOperation(\"更新指定用户编号的用户\")\n    public Boolean update(UserUpdateDTO updateDTO) {\n        // 更新用户记录\n        Boolean success = true;\n        // 返回更新是否成功\n        return success;\n    }\n\n    @PostMapping(\"/delete\")\n    @ApiOperation(value = \"删除指定用户编号的用户\")\n    @ApiImplicitParam(name = \"id\", value = \"用户编号\", paramType = \"query\", dataTypeClass = Integer.class, required = true, example = \"1024\")\n    public Boolean delete(@RequestParam(\"id\") Integer id) {\n        // 删除用户记录\n        Boolean success = false;\n        // 返回是否更新成功\n        return success;\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger-starter/src/main/java/cn/iocoder/springboot/lab24/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springboot.lab24.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\n\n@ApiModel(\"用户添加 DTO\")\npublic class UserAddDTO {\n\n    @ApiModelProperty(value = \"账号\", required = true, example = \"yudaoyuanma\")\n    private String username;\n    @ApiModelProperty(value = \"密码\", required = true, example = \"nicai\")\n    private String password;\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserAddDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserAddDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger-starter/src/main/java/cn/iocoder/springboot/lab24/dto/UserUpdateDTO.java",
    "content": "package cn.iocoder.springboot.lab24.dto;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\n\n@ApiModel(\"用户更新 DTO\")\npublic class UserUpdateDTO {\n\n    @ApiModelProperty(value = \"用户编号\", required = true, example = \"1024\")\n    private Integer id;\n    @ApiModelProperty(value = \"账号\", required = true, example = \"yudaoyuanma\")\n    private String username;\n    @ApiModelProperty(value = \"密码\", required = true, example = \"nicai\")\n    private String password;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserUpdateDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserUpdateDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserUpdateDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger-starter/src/main/java/cn/iocoder/springboot/lab24/vo/UserVO.java",
    "content": "package cn.iocoder.springboot.lab24.vo;\n\nimport io.swagger.annotations.ApiModel;\nimport io.swagger.annotations.ApiModelProperty;\n\n@ApiModel(\"用户 VO\")\npublic class UserVO {\n\n    @ApiModelProperty(value = \"用户编号\", required = true, example = \"1024\")\n    private Integer id;\n    @ApiModelProperty(value = \"账号\", required = true, example = \"yudaoyuanma\")\n    private String username;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserVO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserVO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-24/lab-24-apidoc-swagger-starter/src/main/resources/application.yaml",
    "content": "# 对应 SpringfoxConfigurationProperties 配置类\nspringfox:\n  documentation:\n    swagger-ui:\n      enabled: true # 是否开启 Swagger UI 功能。默认为 true\n"
  },
  {
    "path": "lab-24/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-24</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-24-apidoc-swagger</module>\n        <module>lab-24-apidoc-swagger-knife4j</module>\n        <module>lab-24-apidoc-japidocs</module>\n        <module>lab-24-apidoc-swagger-starter</module>\n        <module>lab-24-apidoc-showdoc</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-24/《芋道 Spring Boot API 接口文档 JApiDocs 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/JApiDocs/?github>\n"
  },
  {
    "path": "lab-24/《芋道 Spring Boot API 接口文档 ShowDoc 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/ShowDoc/?github>\n"
  },
  {
    "path": "lab-24/《芋道 Spring Boot API 接口文档 Swagger Starter 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Swagger-Starter/?github>\n"
  },
  {
    "path": "lab-24/《芋道 Spring Boot API 接口文档 Swagger 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Swagger/?github>\n"
  },
  {
    "path": "lab-25/lab-websocket-25-01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.10.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-websocket-25-01</artifactId>\n\n    <dependencies>\n        <!-- 实现对 WebSocket 相关依赖的引入，方便~ -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-websocket</artifactId>\n        </dependency>\n\n        <!-- 引入 Fastjson ，实现对 JSON 的序列化，因为后续我们会使用它解析消息 -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n            <version>1.2.62</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/Application.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/config/WebSocketConfiguration.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.socket.server.standard.ServerEndpointExporter;\n\n@Configuration\n// @EnableWebSocket // 无需添加该注解，因为我们并不是使用 Spring WebSocket\npublic class WebSocketConfiguration {\n\n    @Bean\n    public ServerEndpointExporter serverEndpointExporter() {\n        return new ServerEndpointExporter();\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/AuthMessageHandler.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.handler;\n\nimport cn.iocoder.springboot.lab25.springwebsocket.message.AuthRequest;\nimport cn.iocoder.springboot.lab25.springwebsocket.message.AuthResponse;\nimport cn.iocoder.springboot.lab25.springwebsocket.message.UserJoinNoticeRequest;\nimport cn.iocoder.springboot.lab25.springwebsocket.util.WebSocketUtil;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.StringUtils;\n\nimport javax.websocket.Session;\n\n@Component\npublic class AuthMessageHandler implements MessageHandler<AuthRequest> {\n\n    @Override\n    public void execute(Session session, AuthRequest message) {\n        // 如果未传递 accessToken\n        if (StringUtils.isEmpty(message.getAccessToken())) {\n            WebSocketUtil.send(session, AuthResponse.TYPE,\n                    new AuthResponse().setCode(1).setMessage(\"认证 accessToken 未传入\"));\n            return;\n        }\n\n        // 添加到 WebSocketUtil 中\n        WebSocketUtil.addSession(session, message.getAccessToken()); // 考虑到代码简化，我们先直接使用 accessToken 作为 User\n\n        // 判断是否认证成功。这里，假装直接成功\n        WebSocketUtil.send(session, AuthResponse.TYPE, new AuthResponse().setCode(0));\n\n        // 通知所有人，某个人加入了。这个是可选逻辑，仅仅是为了演示\n        WebSocketUtil.broadcast(UserJoinNoticeRequest.TYPE,\n                new UserJoinNoticeRequest().setNickname(message.getAccessToken())); // 考虑到代码简化，我们先直接使用 accessToken 作为 User\n    }\n\n    @Override\n    public String getType() {\n        return AuthRequest.TYPE;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/MessageHandler.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.handler;\n\nimport cn.iocoder.springboot.lab25.springwebsocket.message.Message;\n\nimport javax.websocket.Session;\n\n/**\n * 消息处理器接口\n */\npublic interface MessageHandler<T extends Message> {\n\n    /**\n     * 执行处理消息\n     *\n     * @param session 会话\n     * @param message 消息\n     */\n    void execute(Session session, T message);\n\n    /**\n     * @return 消息类型，即每个 Message 实现类上的 TYPE 静态字段\n     */\n    String getType();\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/SendToAllHandler.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.handler;\n\nimport cn.iocoder.springboot.lab25.springwebsocket.message.SendResponse;\nimport cn.iocoder.springboot.lab25.springwebsocket.message.SendToAllRequest;\nimport cn.iocoder.springboot.lab25.springwebsocket.message.SendToUserRequest;\nimport cn.iocoder.springboot.lab25.springwebsocket.util.WebSocketUtil;\nimport org.springframework.stereotype.Component;\n\nimport javax.websocket.Session;\n\n@Component\npublic class SendToAllHandler implements MessageHandler<SendToAllRequest> {\n\n    @Override\n    public void execute(Session session, SendToAllRequest message) {\n        // 这里，假装直接成功\n        SendResponse sendResponse = new SendResponse().setMsgId(message.getMsgId()).setCode(0);\n        WebSocketUtil.send(session, SendResponse.TYPE, sendResponse);\n\n        // 创建转发的消息\n        SendToUserRequest sendToUserRequest = new SendToUserRequest().setMsgId(message.getMsgId())\n                .setContent(message.getContent());\n        // 广播发送\n        WebSocketUtil.broadcast(SendToUserRequest.TYPE, sendToUserRequest);\n    }\n\n    @Override\n    public String getType() {\n        return SendToAllRequest.TYPE;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/SendToOneHandler.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.handler;\n\nimport cn.iocoder.springboot.lab25.springwebsocket.message.SendResponse;\nimport cn.iocoder.springboot.lab25.springwebsocket.message.SendToOneRequest;\nimport cn.iocoder.springboot.lab25.springwebsocket.message.SendToUserRequest;\nimport cn.iocoder.springboot.lab25.springwebsocket.util.WebSocketUtil;\nimport org.springframework.stereotype.Component;\n\nimport javax.websocket.Session;\n\n@Component\npublic class SendToOneHandler implements MessageHandler<SendToOneRequest> {\n\n    @Override\n    public void execute(Session session, SendToOneRequest message) {\n        // 这里，假装直接成功\n        SendResponse sendResponse = new SendResponse().setMsgId(message.getMsgId()).setCode(0);\n        WebSocketUtil.send(session, SendResponse.TYPE, sendResponse);\n\n        // 创建转发的消息\n        SendToUserRequest sendToUserRequest = new SendToUserRequest().setMsgId(message.getMsgId())\n                .setContent(message.getContent());\n        // 广播发送\n        WebSocketUtil.send(message.getToUser(), SendToUserRequest.TYPE, sendToUserRequest);\n    }\n\n    @Override\n    public String getType() {\n        return SendToOneRequest.TYPE;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthRequest.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 用户认证请求\n */\npublic class AuthRequest implements Message {\n\n    public static final String TYPE = \"AUTH_REQUEST\";\n\n    /**\n     * 认证 Token\n     */\n    private String accessToken;\n\n    public String getAccessToken() {\n        return accessToken;\n    }\n\n    public AuthRequest setAccessToken(String accessToken) {\n        this.accessToken = accessToken;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthResponse.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 用户认证响应\n */\npublic class AuthResponse implements Message {\n\n    public static final String TYPE = \"AUTH_RESPONSE\";\n\n    /**\n     * 响应状态码\n     */\n    private Integer code;\n    /**\n     * 响应提示\n     */\n    private String message;\n\n    public Integer getCode() {\n        return code;\n    }\n\n    public AuthResponse setCode(Integer code) {\n        this.code = code;\n        return this;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public AuthResponse setMessage(String message) {\n        this.message = message;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/Message.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 基础消息体\n */\npublic interface Message {\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendResponse.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 发送消息响应结果的 Message\n */\npublic class SendResponse implements Message {\n\n    public static final String TYPE = \"SEND_RESPONSE\";\n\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 响应状态码\n     */\n    private Integer code;\n    /**\n     * 响应提示\n     */\n    private String message;\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public SendResponse setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n    public Integer getCode() {\n        return code;\n    }\n\n    public SendResponse setCode(Integer code) {\n        this.code = code;\n        return this;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public SendResponse setMessage(String message) {\n        this.message = message;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToAllRequest.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 发送给所有人的群聊消息的 Message\n */\npublic class SendToAllRequest implements Message {\n\n    public static final String TYPE = \"SEND_TO_ALL_REQUEST\";\n\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 内容\n     */\n    private String content;\n\n    public String getContent() {\n        return content;\n    }\n\n    public SendToAllRequest setContent(String content) {\n        this.content = content;\n        return this;\n    }\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public SendToAllRequest setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToOneRequest.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 发送给指定人的私聊消息的 Message\n */\npublic class SendToOneRequest implements Message {\n\n    public static final String TYPE = \"SEND_TO_ONE_REQUEST\";\n\n    /**\n     * 发送给的用户\n     */\n    private String toUser;\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 内容\n     */\n    private String content;\n\n    public String getToUser() {\n        return toUser;\n    }\n\n    public SendToOneRequest setToUser(String toUser) {\n        this.toUser = toUser;\n        return this;\n    }\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public SendToOneRequest setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n    public String getContent() {\n        return content;\n    }\n\n    public SendToOneRequest setContent(String content) {\n        this.content = content;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToUserRequest.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 发送消息给一个用户的 Message\n */\npublic class SendToUserRequest implements Message {\n\n    public static final String TYPE = \"SEND_TO_USER_REQUEST\";\n\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 内容\n     */\n    private String content;\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public SendToUserRequest setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n    public String getContent() {\n        return content;\n    }\n\n    public SendToUserRequest setContent(String content) {\n        this.content = content;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/UserJoinNoticeRequest.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 用户加入群聊的通知 Message\n */\npublic class UserJoinNoticeRequest implements Message {\n\n    public static final String TYPE = \"USER_JOIN_NOTICE_REQUEST\";\n\n    /**\n     * 昵称\n     */\n    private String nickname;\n\n    public String getNickname() {\n        return nickname;\n    }\n\n    public UserJoinNoticeRequest setNickname(String nickname) {\n        this.nickname = nickname;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/util/WebSocketUtil.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.util;\n\nimport cn.iocoder.springboot.lab25.springwebsocket.message.Message;\nimport com.alibaba.fastjson.JSONObject;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.websocket.RemoteEndpoint;\nimport javax.websocket.Session;\nimport java.io.IOException;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * WebSocket 工具类，提供客户端连接的管理等功能\n */\npublic class WebSocketUtil {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketUtil.class);\n\n    // ========== 会话相关 ==========\n\n    /**\n     * Session 与用户的映射\n     */\n    private static final Map<Session, String> SESSION_USER_MAP = new ConcurrentHashMap<>();\n    /**\n     * 用户与 Session 的映射\n     */\n    private static final Map<String, Session> USER_SESSION_MAP = new ConcurrentHashMap<>();\n\n    /**\n     * 添加 Session 。在这个方法中，会添加用户和 Session 之间的映射\n     *\n     * @param session Session\n     * @param user 用户\n     */\n    public static void addSession(Session session, String user) {\n        // 更新 USER_SESSION_MAP\n        USER_SESSION_MAP.put(user, session);\n        // 更新 SESSION_USER_MAP\n        SESSION_USER_MAP.put(session, user);\n    }\n\n    /**\n     * 移除 Session 。\n     *\n     * @param session Session\n     */\n    public static void removeSession(Session session) {\n        // 从 SESSION_USER_MAP 中移除\n        String user = SESSION_USER_MAP.remove(session);\n        // 从 USER_SESSION_MAP 中移除\n        if (user != null && user.length() > 0) {\n            USER_SESSION_MAP.remove(user);\n        }\n    }\n\n    // ========== 消息相关 ==========\n\n    /**\n     * 广播发送消息给所有在线用户\n     *\n     * @param type 消息类型\n     * @param message 消息体\n     * @param <T> 消息类型\n     */\n    public static <T extends Message> void broadcast(String type, T message) {\n        // 创建消息\n        String messageText = buildTextMessage(type, message);\n        // 遍历 SESSION_USER_MAP ，进行逐个发送\n        for (Session session : SESSION_USER_MAP.keySet()) {\n            sendTextMessage(session, messageText);\n        }\n    }\n\n    /**\n     * 发送消息给单个用户的 Session\n     *\n     * @param session Session\n     * @param type 消息类型\n     * @param message 消息体\n     * @param <T> 消息类型\n     */\n    public static <T extends Message> void send(Session session, String type, T message) {\n        // 创建消息\n        String messageText = buildTextMessage(type, message);\n        // 遍历给单个 Session ，进行逐个发送\n        sendTextMessage(session, messageText);\n    }\n\n    /**\n     * 发送消息给指定用户\n     *\n     * @param user 指定用户\n     * @param type 消息类型\n     * @param message 消息体\n     * @param <T> 消息类型\n     * @return 发送是否成功你那个\n     */\n    public static <T extends Message> boolean send(String user, String type, T message) {\n        // 获得用户对应的 Session\n        Session session = USER_SESSION_MAP.get(user);\n        if (session == null) {\n            LOGGER.error(\"[send][user({}) 不存在对应的 session]\", user);\n            return false;\n        }\n        // 发送消息\n        send(session, type, message);\n        return true;\n    }\n\n    /**\n     * 构建完整的消息\n     *\n     * @param type 消息类型\n     * @param message 消息体\n     * @param <T> 消息类型\n     * @return 消息\n     */\n    private static <T extends Message> String buildTextMessage(String type, T message) {\n        JSONObject messageObject = new JSONObject();\n        messageObject.put(\"type\", type);\n        messageObject.put(\"body\", message);\n        return messageObject.toString();\n    }\n\n    /**\n     * 真正发送消息\n     *\n     * @param session Session\n     * @param messageText 消息\n     */\n    private static void sendTextMessage(Session session, String messageText) {\n        if (session == null) {\n            LOGGER.error(\"[sendTextMessage][session 为 null]\");\n            return;\n        }\n        RemoteEndpoint.Basic basic = session.getBasicRemote();\n        if (basic == null) {\n            LOGGER.error(\"[sendTextMessage][session 的  为 null]\");\n            return;\n        }\n        try {\n            basic.sendText(messageText);\n        } catch (IOException e) {\n            LOGGER.error(\"[sendTextMessage][session({}) 发送消息{}) 发生异常\",\n                    session, messageText, e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-01/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/websocket/WebsocketServerEndpoint.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.websocket;\n\nimport cn.iocoder.springboot.lab25.springwebsocket.handler.MessageHandler;\nimport cn.iocoder.springboot.lab25.springwebsocket.message.AuthRequest;\nimport cn.iocoder.springboot.lab25.springwebsocket.message.Message;\nimport cn.iocoder.springboot.lab25.springwebsocket.util.WebSocketUtil;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.aop.framework.AopProxyUtils;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.util.CollectionUtils;\n\nimport javax.websocket.*;\nimport javax.websocket.server.ServerEndpoint;\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Objects;\n\n@Controller\n@ServerEndpoint(\"/\")\npublic class WebsocketServerEndpoint implements InitializingBean {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    /**\n     * 消息类型与 MessageHandler 的映射\n     *\n     * 注意，这里设置成静态变量。虽然说 WebsocketServerEndpoint 是单例，但是 Spring Boot 还是会为每个 WebSocket 创建一个 WebsocketServerEndpoint Bean 。\n     */\n    private static final Map<String, MessageHandler> HANDLERS = new HashMap<>();\n\n    @Autowired\n    private ApplicationContext applicationContext;\n\n    @OnOpen\n    public void onOpen(Session session, EndpointConfig config) {\n        logger.info(\"[onOpen][session({}) 接入]\", session);\n        // 解析 accessToken\n        List<String> accessTokenValues = session.getRequestParameterMap().get(\"accessToken\");\n        String accessToken = !CollectionUtils.isEmpty(accessTokenValues) ? accessTokenValues.get(0) : null;\n        // 创建 AuthRequest 消息类型\n        AuthRequest authRequest = new AuthRequest().setAccessToken(accessToken);\n        // 获得消息处理器\n        MessageHandler<AuthRequest> messageHandler = HANDLERS.get(AuthRequest.TYPE);\n        if (messageHandler == null) {\n            logger.error(\"[onOpen][认证消息类型，不存在消息处理器]\");\n            return;\n        }\n        messageHandler.execute(session, authRequest);\n    }\n\n    @OnMessage\n    public void onMessage(Session session, String message) {\n        logger.info(\"[onOpen][session({}) 接收到一条消息({})]\", session, message); // 生产环境下，请设置成 debug 级别\n        try {\n            // 获得消息类型\n            JSONObject jsonMessage = JSON.parseObject(message);\n            String messageType = jsonMessage.getString(\"type\");\n            // 获得消息处理器\n            MessageHandler messageHandler = HANDLERS.get(messageType);\n            if (messageHandler == null) {\n                logger.error(\"[onMessage][消息类型({}) 不存在消息处理器]\", messageType);\n                return;\n            }\n            // 解析消息\n            Class<? extends Message> messageClass = this.getMessageClass(messageHandler);\n            // 处理消息\n            Message messageObj = JSON.parseObject(jsonMessage.getString(\"body\"), messageClass);\n            messageHandler.execute(session, messageObj);\n        } catch (Throwable throwable) {\n            logger.info(\"[onMessage][session({}) message({}) 发生异常]\", session, throwable);\n        }\n    }\n\n    @OnClose\n    public void onClose(Session session, CloseReason closeReason) {\n        logger.info(\"[onClose][session({}) 连接关闭。关闭原因是({})}]\", session, closeReason);\n        WebSocketUtil.removeSession(session);\n    }\n\n    @OnError\n    public void onError(Session session, Throwable throwable) {\n        logger.info(\"[onClose][session({}) 发生异常]\", session, throwable);\n    }\n\n    @Override\n    public void afterPropertiesSet() throws Exception {\n        // 通过 ApplicationContext 获得所有 MessageHandler Bean\n        applicationContext.getBeansOfType(MessageHandler.class).values() // 获得所有 MessageHandler Bean\n                .forEach(messageHandler -> HANDLERS.put(messageHandler.getType(), messageHandler)); // 添加到 handlers 中\n        logger.info(\"[afterPropertiesSet][消息处理器数量：{}]\", HANDLERS.size());\n    }\n\n    private Class<? extends Message> getMessageClass(MessageHandler handler) {\n        // 获得 Bean 对应的 Class 类名。因为有可能被 AOP 代理过。\n        Class<?> targetClass = AopProxyUtils.ultimateTargetClass(handler);\n        // 获得接口的 Type 数组\n        Type[] interfaces = targetClass.getGenericInterfaces();\n        Class<?> superclass = targetClass.getSuperclass();\n        while ((Objects.isNull(interfaces) || 0 == interfaces.length) && Objects.nonNull(superclass)) { // 此处，是以父类的接口为准\n            interfaces = superclass.getGenericInterfaces();\n            superclass = targetClass.getSuperclass();\n        }\n        if (Objects.nonNull(interfaces)) {\n            // 遍历 interfaces 数组\n            for (Type type : interfaces) {\n                // 要求 type 是泛型参数\n                if (type instanceof ParameterizedType) {\n                    ParameterizedType parameterizedType = (ParameterizedType) type;\n                    // 要求是 MessageHandler 接口\n                    if (Objects.equals(parameterizedType.getRawType(), MessageHandler.class)) {\n                        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();\n                        // 取首个元素\n                        if (Objects.nonNull(actualTypeArguments) && actualTypeArguments.length > 0) {\n                            return (Class<Message>) actualTypeArguments[0];\n                        } else {\n                            throw new IllegalStateException(String.format(\"类型(%s) 获得不到消息类型\", handler));\n                        }\n                    }\n                }\n            }\n        }\n        throw new IllegalStateException(String.format(\"类型(%s) 获得不到消息类型\", handler));\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-02/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.10.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-websocket-25-02</artifactId>\n\n    <dependencies>\n        <!-- 实现对 WebSocket 相关依赖的引入，方便~ -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-websocket</artifactId>\n        </dependency>\n\n        <!-- 引入 Fastjson ，实现对 JSON 的序列化，因为后续我们会使用它解析消息 -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n            <version>1.2.62</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/Application.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/config/WebSocketConfiguration.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.config;\n\nimport cn.iocoder.springboot.lab25.springwebsocket.websocket.DemoWebSocketHandler;\nimport cn.iocoder.springboot.lab25.springwebsocket.websocket.DemoWebSocketShakeInterceptor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.socket.config.annotation.EnableWebSocket;\nimport org.springframework.web.socket.config.annotation.WebSocketConfigurer;\nimport org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;\n\n@Configuration\n@EnableWebSocket // 开启 Spring WebSocket\npublic class WebSocketConfiguration implements WebSocketConfigurer {\n\n    @Override\n    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {\n        registry.addHandler(this.webSocketHandler(), \"/\") // 配置处理器\n                .addInterceptors(new DemoWebSocketShakeInterceptor()) // 配置拦截器\n                .setAllowedOrigins(\"*\"); // 解决跨域问题\n    }\n\n    @Bean\n    public DemoWebSocketHandler webSocketHandler() {\n        return new DemoWebSocketHandler();\n    }\n\n    @Bean\n    public DemoWebSocketShakeInterceptor webSocketShakeInterceptor() {\n        return new DemoWebSocketShakeInterceptor();\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/AuthMessageHandler.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.handler;\n\nimport cn.iocoder.springboot.lab25.springwebsocket.message.AuthRequest;\nimport cn.iocoder.springboot.lab25.springwebsocket.message.AuthResponse;\nimport cn.iocoder.springboot.lab25.springwebsocket.message.UserJoinNoticeRequest;\nimport cn.iocoder.springboot.lab25.springwebsocket.util.WebSocketUtil;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.socket.WebSocketSession;\n\n@Component\npublic class AuthMessageHandler implements MessageHandler<AuthRequest> {\n\n    @Override\n    public void execute(WebSocketSession session, AuthRequest message) {\n        // 如果未传递 accessToken\n        if (StringUtils.isEmpty(message.getAccessToken())) {\n            WebSocketUtil.send(session, AuthResponse.TYPE,\n                    new AuthResponse().setCode(1).setMessage(\"认证 accessToken 未传入\"));\n            return;\n        }\n\n        // 添加到 WebSocketUtil 中\n        WebSocketUtil.addSession(session, message.getAccessToken()); // 考虑到代码简化，我们先直接使用 accessToken 作为 User\n\n        // 判断是否认证成功。这里，假装直接成功\n        WebSocketUtil.send(session, AuthResponse.TYPE, new AuthResponse().setCode(0));\n\n        // 通知所有人，某个人加入了。这个是可选逻辑，仅仅是为了演示\n        WebSocketUtil.broadcast(UserJoinNoticeRequest.TYPE,\n                new UserJoinNoticeRequest().setNickname(message.getAccessToken())); // 考虑到代码简化，我们先直接使用 accessToken 作为 User\n    }\n\n    @Override\n    public String getType() {\n        return AuthRequest.TYPE;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/MessageHandler.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.handler;\n\nimport cn.iocoder.springboot.lab25.springwebsocket.message.Message;\nimport org.springframework.web.socket.WebSocketSession;\n\n/**\n * 消息处理器接口\n */\npublic interface MessageHandler<T extends Message> {\n\n    /**\n     * 执行处理消息\n     *\n     * @param session 会话\n     * @param message 消息\n     */\n    void execute(WebSocketSession session, T message);\n\n    /**\n     * @return 消息类型，即每个 Message 实现类上的 TYPE 静态字段\n     */\n    String getType();\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/SendToAllHandler.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.handler;\n\nimport cn.iocoder.springboot.lab25.springwebsocket.message.SendResponse;\nimport cn.iocoder.springboot.lab25.springwebsocket.message.SendToAllRequest;\nimport cn.iocoder.springboot.lab25.springwebsocket.message.SendToUserRequest;\nimport cn.iocoder.springboot.lab25.springwebsocket.util.WebSocketUtil;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.socket.WebSocketSession;\n\n@Component\npublic class SendToAllHandler implements MessageHandler<SendToAllRequest> {\n\n    @Override\n    public void execute(WebSocketSession session, SendToAllRequest message) {\n        // 这里，假装直接成功\n        SendResponse sendResponse = new SendResponse().setMsgId(message.getMsgId()).setCode(0);\n        WebSocketUtil.send(session, SendResponse.TYPE, sendResponse);\n\n        // 创建转发的消息\n        SendToUserRequest sendToUserRequest = new SendToUserRequest().setMsgId(message.getMsgId())\n                .setContent(message.getContent());\n        // 广播发送\n        WebSocketUtil.broadcast(SendToUserRequest.TYPE, sendToUserRequest);\n    }\n\n    @Override\n    public String getType() {\n        return SendToAllRequest.TYPE;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/handler/SendToOneHandler.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.handler;\n\nimport cn.iocoder.springboot.lab25.springwebsocket.message.SendResponse;\nimport cn.iocoder.springboot.lab25.springwebsocket.message.SendToOneRequest;\nimport cn.iocoder.springboot.lab25.springwebsocket.message.SendToUserRequest;\nimport cn.iocoder.springboot.lab25.springwebsocket.util.WebSocketUtil;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.socket.WebSocketSession;\n\n@Component\npublic class SendToOneHandler implements MessageHandler<SendToOneRequest> {\n\n    @Override\n    public void execute(WebSocketSession session, SendToOneRequest message) {\n        // 这里，假装直接成功\n        SendResponse sendResponse = new SendResponse().setMsgId(message.getMsgId()).setCode(0);\n        WebSocketUtil.send(session, SendResponse.TYPE, sendResponse);\n\n        // 创建转发的消息\n        SendToUserRequest sendToUserRequest = new SendToUserRequest().setMsgId(message.getMsgId())\n                .setContent(message.getContent());\n        // 广播发送\n        WebSocketUtil.send(message.getToUser(), SendToUserRequest.TYPE, sendToUserRequest);\n    }\n\n    @Override\n    public String getType() {\n        return SendToOneRequest.TYPE;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthRequest.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 用户认证请求\n */\npublic class AuthRequest implements Message {\n\n    public static final String TYPE = \"AUTH_REQUEST\";\n\n    /**\n     * 认证 Token\n     */\n    private String accessToken;\n\n    public String getAccessToken() {\n        return accessToken;\n    }\n\n    public AuthRequest setAccessToken(String accessToken) {\n        this.accessToken = accessToken;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthResponse.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 用户认证响应\n */\npublic class AuthResponse implements Message {\n\n    public static final String TYPE = \"AUTH_RESPONSE\";\n\n    /**\n     * 响应状态码\n     */\n    private Integer code;\n    /**\n     * 响应提示\n     */\n    private String message;\n\n    public Integer getCode() {\n        return code;\n    }\n\n    public AuthResponse setCode(Integer code) {\n        this.code = code;\n        return this;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public AuthResponse setMessage(String message) {\n        this.message = message;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/Message.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 基础消息体\n */\npublic interface Message {\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendResponse.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 发送消息响应结果的 Message\n */\npublic class SendResponse implements Message {\n\n    public static final String TYPE = \"SEND_RESPONSE\";\n\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 响应状态码\n     */\n    private Integer code;\n    /**\n     * 响应提示\n     */\n    private String message;\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public SendResponse setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n    public Integer getCode() {\n        return code;\n    }\n\n    public SendResponse setCode(Integer code) {\n        this.code = code;\n        return this;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public SendResponse setMessage(String message) {\n        this.message = message;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToAllRequest.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 发送给所有人的群聊消息的 Message\n */\npublic class SendToAllRequest implements Message {\n\n    public static final String TYPE = \"SEND_TO_ALL_REQUEST\";\n\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 内容\n     */\n    private String content;\n\n    public String getContent() {\n        return content;\n    }\n\n    public SendToAllRequest setContent(String content) {\n        this.content = content;\n        return this;\n    }\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public SendToAllRequest setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToOneRequest.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 发送给指定人的私聊消息的 Message\n */\npublic class SendToOneRequest implements Message {\n\n    public static final String TYPE = \"SEND_TO_ONE_REQUEST\";\n\n    /**\n     * 发送给的用户\n     */\n    private String toUser;\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 内容\n     */\n    private String content;\n\n    public String getToUser() {\n        return toUser;\n    }\n\n    public SendToOneRequest setToUser(String toUser) {\n        this.toUser = toUser;\n        return this;\n    }\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public SendToOneRequest setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n    public String getContent() {\n        return content;\n    }\n\n    public SendToOneRequest setContent(String content) {\n        this.content = content;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToUserRequest.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 发送消息给一个用户的 Message\n */\npublic class SendToUserRequest implements Message {\n\n    public static final String TYPE = \"SEND_TO_USER_REQUEST\";\n\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 内容\n     */\n    private String content;\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public SendToUserRequest setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n    public String getContent() {\n        return content;\n    }\n\n    public SendToUserRequest setContent(String content) {\n        this.content = content;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/UserJoinNoticeRequest.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 用户加入群聊的通知 Message\n */\npublic class UserJoinNoticeRequest implements Message {\n\n    public static final String TYPE = \"USER_JOIN_NOTICE_REQUEST\";\n\n    /**\n     * 昵称\n     */\n    private String nickname;\n\n    public String getNickname() {\n        return nickname;\n    }\n\n    public UserJoinNoticeRequest setNickname(String nickname) {\n        this.nickname = nickname;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/util/WebSocketUtil.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.util;\n\nimport cn.iocoder.springboot.lab25.springwebsocket.message.Message;\nimport com.alibaba.fastjson.JSONObject;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.socket.TextMessage;\nimport org.springframework.web.socket.WebSocketSession;\n\nimport java.io.IOException;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * WebSocket 工具类，提供客户端连接的管理等功能\n */\npublic class WebSocketUtil {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketUtil.class);\n\n    // ========== 会话相关 ==========\n\n    /**\n     * Session 与用户的映射\n     */\n    private static final Map<WebSocketSession, String> SESSION_USER_MAP = new ConcurrentHashMap<>();\n    /**\n     * 用户与 Session 的映射\n     */\n    private static final Map<String, WebSocketSession> USER_SESSION_MAP = new ConcurrentHashMap<>();\n\n    /**\n     * 添加 Session 。在这个方法中，会添加用户和 Session 之间的映射\n     *\n     * @param session Session\n     * @param user 用户\n     */\n    public static void addSession(WebSocketSession session, String user) {\n        // 更新 USER_SESSION_MAP\n        USER_SESSION_MAP.put(user, session);\n        // 更新 SESSION_USER_MAP\n        SESSION_USER_MAP.put(session, user);\n    }\n\n    /**\n     * 移除 Session 。\n     *\n     * @param session Session\n     */\n    public static void removeSession(WebSocketSession session) {\n        // 从 SESSION_USER_MAP 中移除\n        String user = SESSION_USER_MAP.remove(session);\n        // 从 USER_SESSION_MAP 中移除\n        if (user != null && user.length() > 0) {\n            USER_SESSION_MAP.remove(user);\n        }\n    }\n\n    // ========== 消息相关 ==========\n\n    /**\n     * 广播发送消息给所有在线用户\n     *\n     * @param type 消息类型\n     * @param message 消息体\n     * @param <T> 消息类型\n     */\n    public static <T extends Message> void broadcast(String type, T message) {\n        // 创建消息\n        TextMessage textMessage = buildTextMessage(type, message);\n        // 遍历 SESSION_USER_MAP ，进行逐个发送\n        for (WebSocketSession session : SESSION_USER_MAP.keySet()) {\n            sendTextMessage(session, textMessage);\n        }\n    }\n\n    /**\n     * 发送消息给单个用户的 Session\n     *\n     * @param session Session\n     * @param type 消息类型\n     * @param message 消息体\n     * @param <T> 消息类型\n     */\n    public static <T extends Message> void send(WebSocketSession session, String type, T message) {\n        // 创建消息\n        TextMessage textMessage = buildTextMessage(type, message);\n        // 遍历给单个 Session ，进行逐个发送\n        sendTextMessage(session, textMessage);\n    }\n\n    /**\n     * 发送消息给指定用户\n     *\n     * @param user 指定用户\n     * @param type 消息类型\n     * @param message 消息体\n     * @param <T> 消息类型\n     * @return 发送是否成功你那个\n     */\n    public static <T extends Message> boolean send(String user, String type, T message) {\n        // 获得用户对应的 Session\n        WebSocketSession session = USER_SESSION_MAP.get(user);\n        if (session == null) {\n            LOGGER.error(\"[send][user({}) 不存在对应的 session]\", user);\n            return false;\n        }\n        // 发送消息\n        send(session, type, message);\n        return true;\n    }\n\n    /**\n     * 构建完整的消息\n     *\n     * @param type 消息类型\n     * @param message 消息体\n     * @param <T> 消息类型\n     * @return 消息\n     */\n    private static <T extends Message> TextMessage buildTextMessage(String type, T message) {\n        JSONObject messageObject = new JSONObject();\n        messageObject.put(\"type\", type);\n        messageObject.put(\"body\", message);\n        return new TextMessage(messageObject.toString());\n    }\n\n    /**\n     * 真正发送消息\n     *\n     * @param session Session\n     * @param textMessage 消息\n     */\n    private static void sendTextMessage(WebSocketSession session, TextMessage textMessage) {\n        if (session == null) {\n            LOGGER.error(\"[sendTextMessage][session 为 null]\");\n            return;\n        }\n        try {\n            session.sendMessage(textMessage);\n        } catch (IOException e) {\n            LOGGER.error(\"[sendTextMessage][session({}) 发送消息{}) 发生异常\",\n                    session, textMessage, e);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/websocket/DemoWebSocketHandler.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.websocket;\n\nimport cn.iocoder.springboot.lab25.springwebsocket.handler.MessageHandler;\nimport cn.iocoder.springboot.lab25.springwebsocket.message.AuthRequest;\nimport cn.iocoder.springboot.lab25.springwebsocket.message.Message;\nimport cn.iocoder.springboot.lab25.springwebsocket.util.WebSocketUtil;\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.aop.framework.AopProxyUtils;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.web.socket.CloseStatus;\nimport org.springframework.web.socket.TextMessage;\nimport org.springframework.web.socket.WebSocketSession;\nimport org.springframework.web.socket.handler.TextWebSocketHandler;\n\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\npublic class DemoWebSocketHandler extends TextWebSocketHandler implements InitializingBean {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    /**\n     * 消息类型与 MessageHandler 的映射\n     *\n     * 无需设置成静态变量\n     */\n    private final Map<String, MessageHandler> HANDLERS = new HashMap<>();\n\n    @Autowired\n    private ApplicationContext applicationContext;\n\n    @Override // 对应 open 事件\n    public void afterConnectionEstablished(WebSocketSession session) throws Exception {\n        logger.info(\"[afterConnectionEstablished][session({}) 接入]\", session);\n        // 解析 accessToken\n        String accessToken = (String) session.getAttributes().get(\"accessToken\");\n        // 创建 AuthRequest 消息类型\n        AuthRequest authRequest = new AuthRequest().setAccessToken(accessToken);\n        // 获得消息处理器\n        MessageHandler<AuthRequest> messageHandler = HANDLERS.get(AuthRequest.TYPE);\n        if (messageHandler == null) {\n            logger.error(\"[onOpen][认证消息类型，不存在消息处理器]\");\n            return;\n        }\n        messageHandler.execute(session, authRequest);\n    }\n\n    @Override // 对应 message 事件\n    public void handleTextMessage(WebSocketSession session, TextMessage textMessage) throws Exception {\n        logger.info(\"[handleMessage][session({}) 接收到一条消息({})]\", session, textMessage); // 生产环境下，请设置成 debug 级别\n        try {\n            // 获得消息类型\n            JSONObject jsonMessage = JSON.parseObject(textMessage.getPayload());\n            String messageType = jsonMessage.getString(\"type\");\n            // 获得消息处理器\n            MessageHandler messageHandler = HANDLERS.get(messageType);\n            if (messageHandler == null) {\n                logger.error(\"[onMessage][消息类型({}) 不存在消息处理器]\", messageType);\n                return;\n            }\n            // 解析消息\n            Class<? extends Message> messageClass = this.getMessageClass(messageHandler);\n            // 处理消息\n            Message messageObj = JSON.parseObject(jsonMessage.getString(\"body\"), messageClass);\n            messageHandler.execute(session, messageObj);\n        } catch (Throwable throwable) {\n            logger.info(\"[onMessage][session({}) message({}) 发生异常]\", session, throwable);\n        }\n    }\n\n    @Override // 对应 close 事件\n    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {\n        logger.info(\"[afterConnectionClosed][session({}) 连接关闭。关闭原因是({})}]\", session, status);\n        WebSocketUtil.removeSession(session);\n    }\n\n    @Override // 对应 error 事件\n    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {\n        logger.info(\"[handleTransportError][session({}) 发生异常]\", session, exception);\n    }\n\n    @Override\n    public void afterPropertiesSet() throws Exception {\n        // 通过 ApplicationContext 获得所有 MessageHandler Bean\n        applicationContext.getBeansOfType(MessageHandler.class).values() // 获得所有 MessageHandler Bean\n                .forEach(messageHandler -> HANDLERS.put(messageHandler.getType(), messageHandler)); // 添加到 handlers 中\n        logger.info(\"[afterPropertiesSet][消息处理器数量：{}]\", HANDLERS.size());\n    }\n\n    private Class<? extends Message> getMessageClass(MessageHandler handler) {\n        // 获得 Bean 对应的 Class 类名。因为有可能被 AOP 代理过。\n        Class<?> targetClass = AopProxyUtils.ultimateTargetClass(handler);\n        // 获得接口的 Type 数组\n        Type[] interfaces = targetClass.getGenericInterfaces();\n        Class<?> superclass = targetClass.getSuperclass();\n        while ((Objects.isNull(interfaces) || 0 == interfaces.length) && Objects.nonNull(superclass)) { // 此处，是以父类的接口为准\n            interfaces = superclass.getGenericInterfaces();\n            superclass = targetClass.getSuperclass();\n        }\n        if (Objects.nonNull(interfaces)) {\n            // 遍历 interfaces 数组\n            for (Type type : interfaces) {\n                // 要求 type 是泛型参数\n                if (type instanceof ParameterizedType) {\n                    ParameterizedType parameterizedType = (ParameterizedType) type;\n                    // 要求是 MessageHandler 接口\n                    if (Objects.equals(parameterizedType.getRawType(), MessageHandler.class)) {\n                        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();\n                        // 取首个元素\n                        if (Objects.nonNull(actualTypeArguments) && actualTypeArguments.length > 0) {\n                            return (Class<Message>) actualTypeArguments[0];\n                        } else {\n                            throw new IllegalStateException(String.format(\"类型(%s) 获得不到消息类型\", handler));\n                        }\n                    }\n                }\n            }\n        }\n        throw new IllegalStateException(String.format(\"类型(%s) 获得不到消息类型\", handler));\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-02/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/websocket/DemoWebSocketShakeInterceptor.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.websocket;\n\nimport org.springframework.http.server.ServerHttpRequest;\nimport org.springframework.http.server.ServerHttpResponse;\nimport org.springframework.http.server.ServletServerHttpRequest;\nimport org.springframework.web.socket.WebSocketHandler;\nimport org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;\n\nimport java.util.Map;\n\n/**\n * 自定义 HttpSessionHandshakeInterceptor 拦截器\n *\n * 因为 WebSocketSession 无法获得 ws 地址上的请求参数，所以只好通过该拦截器，获得 accessToken 请求参数，设置到 attributes 中\n */\npublic class DemoWebSocketShakeInterceptor extends HttpSessionHandshakeInterceptor {\n\n    @Override // 拦截 Handshake 事件\n    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,\n                                   WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {\n        // 获得 accessToken\n        if (request instanceof ServletServerHttpRequest) {\n            ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;\n            attributes.put(\"accessToken\", serverRequest.getServletRequest().getParameter(\"accessToken\"));\n        }\n        // 调用父方法，继续执行逻辑\n        return super.beforeHandshake(request, response, wsHandler, attributes);\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-03/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.10.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-websocket-25-03</artifactId>\n\n    <dependencies>\n        <!-- 实现对 WebSocket 相关依赖的引入，方便~ -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-websocket</artifactId>\n        </dependency>\n\n        <!-- 引入 Fastjson ，实现对 JSON 的序列化，因为后续我们会使用它解析消息 -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n            <version>1.2.62</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/Application.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/config/WebSocketConfiguration.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.config;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.messaging.simp.config.MessageBrokerRegistry;\nimport org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;\nimport org.springframework.web.socket.config.annotation.StompEndpointRegistry;\nimport org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;\n\n@Configuration\n@EnableWebSocketMessageBroker // 开启 Spring WebSocket 对 Stomp 的支持\npublic class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {\n\n    @Override\n    public void configureMessageBroker(MessageBrokerRegistry registry) {\n        registry.enableSimpleBroker(\"/topic\");\n        registry.setApplicationDestinationPrefixes(\"/app\");\n    }\n\n    @Override\n    public void registerStompEndpoints(StompEndpointRegistry registry) {\n//        registry.addEndpoint(\"/send_to_all\")\n//                .setAllowedOrigins(\"*\")\n//                .withSockJS();\n        registry.addEndpoint(\"/\")\n                .setAllowedOrigins(\"*\")\n                .withSockJS();\n\n//        RequestUpgradeStrategy upgradeStrategy = new TomcatRequestUpgradeStrategy();\n//        registry.addEndpoint(\"/\")\n//                .setHandshakeHandler(new DefaultHandshakeHandler(upgradeStrategy))\n//                .setAllowedOrigins(\"*\");\n    }\n\n//    @Override\n//    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {\n//        registry.addHandler(this.webSocketHandler(), \"/\") // 配置处理器\n//                .addInterceptors(new DemoWebSocketShakeInterceptor()) // 配置拦截器\n//                .setAllowedOrigins(\"*\"); // 解决跨域问题\n//    }\n\n//    @Bean\n//    public DemoWebSocketHandler webSocketHandler() {\n//        return new DemoWebSocketHandler();\n//    }\n\n//    @Bean\n//    public DemoWebSocketShakeInterceptor webSocketShakeInterceptor() {\n//        return new DemoWebSocketShakeInterceptor();\n//    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/controller/SendController.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.controller;\n\nimport cn.iocoder.springboot.lab25.springwebsocket.message.SendToAllRequest;\nimport cn.iocoder.springboot.lab25.springwebsocket.message.SendToUserRequest;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.messaging.handler.annotation.MessageMapping;\nimport org.springframework.messaging.handler.annotation.SendTo;\nimport org.springframework.stereotype.Controller;\n\n@Controller\npublic class SendController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @MessageMapping(\"/send_to_all\")\n    @SendTo(value = \"/topic/send_to_all\") // TODO https://blog.csdn.net/fly_leopard/article/details/78664409 暂时先跳过\n    public SendToUserRequest sendToAll(SendToAllRequest message) {\n        logger.info(\"[sendToAll][SendToAllRequest({})]\", message);\n//        // 创建转发的消息\n//        SendToUserRequest sendToUserRequest = new SendToUserRequest().setMsgId(message.getMsgId())\n//                .setContent(message.getContent());\n//        // 广播发送\n//        WebSocketUtil.broadcast(SendToUserRequest.TYPE, sendToUserRequest);\n\n        // 这里，假装直接成功\n//        return new SendResponse().setMsgId(message.getMsgId()).setCode(0);\n        return new SendToUserRequest().setMsgId(message.getMsgId())\n                .setContent(message.getContent());\n    }\n\n//    @SubscribeMapping(\"/topic/send_to_all\")\n//    public void subSendToAll(SendToUserRequest message) {\n//        logger.info(\"[subSendToAll][SendToUserRequest({})]\", message);\n//    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthRequest.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 用户认证请求\n */\npublic class AuthRequest implements Message {\n\n    public static final String TYPE = \"AUTH_REQUEST\";\n\n    /**\n     * 认证 Token\n     */\n    private String accessToken;\n\n    public String getAccessToken() {\n        return accessToken;\n    }\n\n    public AuthRequest setAccessToken(String accessToken) {\n        this.accessToken = accessToken;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/AuthResponse.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 用户认证响应\n */\npublic class AuthResponse implements Message {\n\n    public static final String TYPE = \"AUTH_RESPONSE\";\n\n    /**\n     * 响应状态码\n     */\n    private Integer code;\n    /**\n     * 响应提示\n     */\n    private String message;\n\n    public Integer getCode() {\n        return code;\n    }\n\n    public AuthResponse setCode(Integer code) {\n        this.code = code;\n        return this;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public AuthResponse setMessage(String message) {\n        this.message = message;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/Message.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 基础消息体\n */\npublic interface Message {\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendResponse.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 发送消息响应结果的 Message\n */\npublic class SendResponse implements Message {\n\n    public static final String TYPE = \"SEND_RESPONSE\";\n\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 响应状态码\n     */\n    private Integer code;\n    /**\n     * 响应提示\n     */\n    private String message;\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public SendResponse setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n    public Integer getCode() {\n        return code;\n    }\n\n    public SendResponse setCode(Integer code) {\n        this.code = code;\n        return this;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public SendResponse setMessage(String message) {\n        this.message = message;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToAllRequest.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 发送给所有人的群聊消息的 Message\n */\npublic class SendToAllRequest implements Message {\n\n    public static final String TYPE = \"SEND_TO_ALL_REQUEST\";\n\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 内容\n     */\n    private String content;\n\n    public String getContent() {\n        return content;\n    }\n\n    public SendToAllRequest setContent(String content) {\n        this.content = content;\n        return this;\n    }\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public SendToAllRequest setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToOneRequest.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 发送给指定人的私聊消息的 Message\n */\npublic class SendToOneRequest implements Message {\n\n    public static final String TYPE = \"SEND_TO_ONE_REQUEST\";\n\n    /**\n     * 发送给的用户\n     */\n    private String toUser;\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 内容\n     */\n    private String content;\n\n    public String getToUser() {\n        return toUser;\n    }\n\n    public SendToOneRequest setToUser(String toUser) {\n        this.toUser = toUser;\n        return this;\n    }\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public SendToOneRequest setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n    public String getContent() {\n        return content;\n    }\n\n    public SendToOneRequest setContent(String content) {\n        this.content = content;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/SendToUserRequest.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 发送消息给一个用户的 Message\n */\npublic class SendToUserRequest implements Message {\n\n    public static final String TYPE = \"SEND_TO_USER_REQUEST\";\n\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 内容\n     */\n    private String content;\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public SendToUserRequest setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n    public String getContent() {\n        return content;\n    }\n\n    public SendToUserRequest setContent(String content) {\n        this.content = content;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-03/src/main/java/cn/iocoder/springboot/lab25/springwebsocket/message/UserJoinNoticeRequest.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.message;\n\n/**\n * 用户加入群聊的通知 Message\n */\npublic class UserJoinNoticeRequest implements Message {\n\n    public static final String TYPE = \"USER_JOIN_NOTICE_REQUEST\";\n\n    /**\n     * 昵称\n     */\n    private String nickname;\n\n    public String getNickname() {\n        return nickname;\n    }\n\n    public UserJoinNoticeRequest setNickname(String nickname) {\n        this.nickname = nickname;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-03/src/test/java/cn/iocoder/springboot/lab25/springwebsocket/Main.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket;\n\nimport cn.iocoder.springboot.lab25.springwebsocket.message.SendToAllRequest;\nimport org.springframework.messaging.converter.MappingJackson2MessageConverter;\nimport org.springframework.messaging.simp.stomp.StompCommand;\nimport org.springframework.messaging.simp.stomp.StompHeaders;\nimport org.springframework.messaging.simp.stomp.StompSession;\nimport org.springframework.messaging.simp.stomp.StompSessionHandler;\nimport org.springframework.web.socket.client.WebSocketClient;\nimport org.springframework.web.socket.client.standard.StandardWebSocketClient;\nimport org.springframework.web.socket.messaging.WebSocketStompClient;\nimport org.springframework.web.socket.sockjs.client.SockJsClient;\nimport org.springframework.web.socket.sockjs.client.Transport;\nimport org.springframework.web.socket.sockjs.client.WebSocketTransport;\n\nimport java.lang.reflect.Type;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Scanner;\nimport java.util.UUID;\n\npublic class Main {\n\n    public static void main(String[] args) {\n        List<Transport> transports = new ArrayList<>(1);\n        transports.add(new WebSocketTransport( new StandardWebSocketClient()) );\n        WebSocketClient transport = new SockJsClient(transports); // TODO 芋艿，参考 https://codeday.me/bug/20190115/521240.html 文章\n//        WebSocketClient client = new StandardWebSocketClient();\n\n        WebSocketStompClient stompClient = new WebSocketStompClient(transport);\n        stompClient.setMessageConverter(new MappingJackson2MessageConverter());\n\n        StompSessionHandler sessionHandler = new StompSessionHandler() {\n\n            @Override\n            public void afterConnected(StompSession stompSession, StompHeaders stompHeaders) {\n//                stompSession.subscribe(\"/topic/send_to_all\", this);\n                stompSession.send(\"/app/send_to_all\", new SendToAllRequest().setMsgId(UUID.randomUUID().toString())\n                    .setContent(\"测试消息\"));\n            }\n\n            @Override\n            public void handleException(StompSession stompSession, StompCommand stompCommand, StompHeaders stompHeaders, byte[] bytes, Throwable throwable) {\n                System.out.println();\n            }\n\n            @Override\n            public void handleTransportError(StompSession stompSession, Throwable throwable) {\n                System.out.println();\n            }\n\n            @Override\n            public Type getPayloadType(StompHeaders stompHeaders) {\n                return null;\n            }\n\n            @Override\n            public void handleFrame(StompHeaders stompHeaders, Object o) {\n                System.out.println();\n            }\n        };\n\n        stompClient.connect(\"ws://127.0.0.1:8080/\", sessionHandler);\n\n        new Scanner(System.in).nextLine(); // Don't close immediately.\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-03/src/test/java/cn/iocoder/springboot/lab25/springwebsocket/client/DemoWebSocketStompClient.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.client;\n\nimport cn.iocoder.springboot.lab25.springwebsocket.client.handler.ConnectHandler;\nimport cn.iocoder.springboot.lab25.springwebsocket.message.SendToAllRequest;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.messaging.converter.MappingJackson2MessageConverter;\nimport org.springframework.messaging.simp.stomp.StompSession;\nimport org.springframework.util.concurrent.ListenableFuture;\nimport org.springframework.web.socket.client.WebSocketClient;\nimport org.springframework.web.socket.client.standard.StandardWebSocketClient;\nimport org.springframework.web.socket.messaging.WebSocketStompClient;\nimport org.springframework.web.socket.sockjs.client.SockJsClient;\nimport org.springframework.web.socket.sockjs.client.Transport;\nimport org.springframework.web.socket.sockjs.client.WebSocketTransport;\n\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Scanner;\nimport java.util.UUID;\nimport java.util.concurrent.ExecutionException;\n\npublic class DemoWebSocketStompClient {\n\n    private static final Logger LOGGER = LoggerFactory.getLogger(DemoWebSocketStompClient.class);\n\n    private WebSocketStompClient stompClient;\n\n    public DemoWebSocketStompClient() {\n        // 创建 SockJsClient 对象，内嵌 StandardWebSocketClient 对象\n        List<Transport> transports = Collections.singletonList(new WebSocketTransport(new StandardWebSocketClient()));\n        WebSocketClient transport = new SockJsClient(transports);\n        // 创建 WebSocketStompClient 对象，内嵌 SockJsClient 对象\n        this.stompClient = new WebSocketStompClient(transport);\n        // 设置消息转换器\n        this.stompClient.setMessageConverter(new MappingJackson2MessageConverter());\n    }\n\n    public ListenableFuture<StompSession> connect(String url) {\n        return this.stompClient.connect(url, new ConnectHandler());\n    }\n\n    public static void main(String[] args) throws ExecutionException, InterruptedException {\n        // 创建 DemoWebSocketStompClient 客户端\n        DemoWebSocketStompClient demoWebSocketStompClient = new DemoWebSocketStompClient();\n        // 发起连接\n        ListenableFuture<StompSession> future = demoWebSocketStompClient.connect(\"ws://127.0.0.1:8080/\");\n        StompSession session = future.get();\n        // 发起消息\n        session.send(\"/app/send_to_all\", new SendToAllRequest().setMsgId(UUID.randomUUID().toString())\n                .setContent(\"测试消息\"));\n\n        // 阻塞等待\n        new Scanner(System.in).nextLine(); // Don't close immediately.\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-03/src/test/java/cn/iocoder/springboot/lab25/springwebsocket/client/handler/ConnectHandler.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.client.handler;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.messaging.simp.stomp.StompCommand;\nimport org.springframework.messaging.simp.stomp.StompHeaders;\nimport org.springframework.messaging.simp.stomp.StompSession;\nimport org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;\n\npublic class ConnectHandler extends StompSessionHandlerAdapter {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void afterConnected(StompSession session, StompHeaders connectedHeaders) {\n        logger.info(\"[afterConnected][session({}) 连接成功 connectedHeaders({})]\",\n                session, connectedHeaders);\n\n        session.subscribe(\"/topic/send_to_all\", new SendToUserRequestHandler());\n    }\n\n    @Override\n    public void handleException(StompSession session, StompCommand command, StompHeaders headers, byte[] payload, Throwable exception) {\n        logger.error(\"[handleException][session({}) command({}) headers({}) payload({}) 发生异常]\",\n                session, command, headers, payload, exception);\n    }\n\n    @Override\n    public void handleTransportError(StompSession session, Throwable exception) {\n        logger.error(\"[handleTransportError][session({}) 发生传输错误]\", session, exception);\n    }\n\n}\n"
  },
  {
    "path": "lab-25/lab-websocket-25-03/src/test/java/cn/iocoder/springboot/lab25/springwebsocket/client/handler/SendToUserRequestHandler.java",
    "content": "package cn.iocoder.springboot.lab25.springwebsocket.client.handler;\n\nimport cn.iocoder.springboot.lab25.springwebsocket.message.SendToUserRequest;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.messaging.simp.stomp.StompHeaders;\nimport org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;\n\nimport java.lang.reflect.Type;\n\npublic class SendToUserRequestHandler extends StompSessionHandlerAdapter {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public Type getPayloadType(StompHeaders headers) {\n        return SendToUserRequest.class;\n    }\n\n    @Override\n    public void handleFrame(StompHeaders headers, Object payload) {\n        SendToUserRequest request = (SendToUserRequest) payload;\n        logger.info(\"[handleFrame][接收到消息 headers({}) payload({})]\", headers, request);\n    }\n}\n"
  },
  {
    "path": "lab-25/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-25</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-websocket-25-01</module>\n        <module>lab-websocket-25-02</module>\n        <module>lab-websocket-25-03</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-25/《芋道 Spring Boot WebSocket 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/WebSocket/?github>\n"
  },
  {
    "path": "lab-26/lab-26-distributed-session-01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.10.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-26-distributed-session-01</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Session 使用 Redis 作为数据源的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.session</groupId>\n            <artifactId>spring-session-data-redis</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Data Redis 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis</artifactId>\n            <exclusions>\n                <!-- 去掉对 Lettuce 的依赖，因为 Spring Boot 优先使用 Lettuce 作为 Redis 客户端 -->\n                <exclusion>\n                    <groupId>io.lettuce</groupId>\n                    <artifactId>lettuce-core</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n        <!-- 引入 Jedis 的依赖，这样 Spring Boot 实现对 Jedis 的自动化配置 -->\n        <dependency>\n            <groupId>redis.clients</groupId>\n            <artifactId>jedis</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-26/lab-26-distributed-session-01/src/main/java/cn/iocoder/springboot/lab26/distributedsession/Application.java",
    "content": "package cn.iocoder.springboot.lab26.distributedsession;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-26/lab-26-distributed-session-01/src/main/java/cn/iocoder/springboot/lab26/distributedsession/config/SessionConfiguration.java",
    "content": "package cn.iocoder.springboot.lab26.distributedsession.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.serializer.RedisSerializer;\nimport org.springframework.session.data.redis.RedisOperationsSessionRepository;\nimport org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;\nimport org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration;\n\n@Configuration\n@EnableRedisHttpSession // 自动化配置 Spring Session 使用 Redis 作为数据源\npublic class SessionConfiguration {\n\n    /**\n     * 创建 {@link RedisOperationsSessionRepository} 使用的 RedisSerializer Bean 。\n     *\n     * 具体可以看看 {@link RedisHttpSessionConfiguration#setDefaultRedisSerializer(RedisSerializer)} 方法，\n     * 它会引入名字为 \"springSessionDefaultRedisSerializer\" 的 Bean 。\n     *\n     * @return RedisSerializer Bean\n     */\n    @Bean(name = \"springSessionDefaultRedisSerializer\")\n    public RedisSerializer springSessionDefaultRedisSerializer() {\n        return RedisSerializer.json();\n    }\n\n//    @Bean\n//    public CookieHttpSessionIdResolver sessionIdResolver() {\n//        // 创建 CookieHttpSessionIdResolver 对象\n//        CookieHttpSessionIdResolver sessionIdResolver = new CookieHttpSessionIdResolver();\n//\n//        // 创建 DefaultCookieSerializer 对象\n//        DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();\n//        sessionIdResolver.setCookieSerializer(cookieSerializer); // 设置到 sessionIdResolver 中\n//        cookieSerializer.setCookieName(\"JSESSIONID\");\n//\n//        return sessionIdResolver;\n//    }\n\n//    @Bean\n//    public HeaderHttpSessionIdResolver sessionIdResolver() {\n////        return HeaderHttpSessionIdResolver.xAuthToken();\n////        return HeaderHttpSessionIdResolver.authenticationInfo();\n//        return new HeaderHttpSessionIdResolver(\"token\");\n//    }\n\n}\n"
  },
  {
    "path": "lab-26/lab-26-distributed-session-01/src/main/java/cn/iocoder/springboot/lab26/distributedsession/controller/SessionController.java",
    "content": "package cn.iocoder.springboot.lab26.distributedsession.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.servlet.http.HttpSession;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/session\")\npublic class SessionController {\n\n    @GetMapping(\"/set\") // 其实 PostMapping 更合适，单纯为了方便\n    public void set(HttpSession session,\n                    @RequestParam(\"key\") String key,\n                    @RequestParam(\"value\") String value) {\n        session.setAttribute(key, value);\n    }\n\n    @GetMapping(\"/get_all\")\n    public Map<String, Object> getAll(HttpSession session) {\n        Map<String, Object> result = new HashMap<>();\n        // 遍历\n        for (Enumeration<String> enumeration = session.getAttributeNames();\n             enumeration.hasMoreElements();) {\n            String key = enumeration.nextElement();\n            Object value = session.getAttribute(key);\n            result.put(key, value);\n        }\n        // 返回\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "lab-26/lab-26-distributed-session-01/src/main/resources/application.yaml",
    "content": "spring:\n  # 对应 RedisProperties 类\n  redis:\n    host: 127.0.0.1\n    port: 6379\n    password: # Redis 服务器密码，默认为空。生产中，一定要设置 Redis 密码！\n    database: 0 # Redis 数据库号，默认为 0 。\n    timeout: 0 # Redis 连接超时时间，单位：毫秒。\n    # 对应 RedisProperties.Jedis 内部类\n    jedis:\n      pool:\n        max-active: 8 # 连接池最大连接数，默认为 8 。使用负数表示没有限制。\n        max-idle: 8 # 默认连接数最大空闲的连接数，默认为 8 。使用负数表示没有限制。\n        min-idle: 0 # 默认连接池最小空闲的连接数，默认为 0 。允许设置 0 和 正数。\n        max-wait: -1 # 连接池最大阻塞等待时间，单位：毫秒。默认为 -1 ，表示不限制。\n"
  },
  {
    "path": "lab-26/lab-26-distributed-session-02/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.10.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-26-distributed-session-02</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Session 使用 MongoDB 作为数据源的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.session</groupId>\n            <artifactId>spring-session-data-mongodb</artifactId>\n        </dependency>\n\n        <!-- 自动化配置 Spring Data Mongodb -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-mongodb</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-26/lab-26-distributed-session-02/src/main/java/cn/iocoder/springboot/lab26/distributedsession/Application.java",
    "content": "package cn.iocoder.springboot.lab26.distributedsession;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-26/lab-26-distributed-session-02/src/main/java/cn/iocoder/springboot/lab26/distributedsession/config/SessionConfiguration.java",
    "content": "package cn.iocoder.springboot.lab26.distributedsession.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.session.data.mongo.AbstractMongoSessionConverter;\nimport org.springframework.session.data.mongo.JacksonMongoSessionConverter;\nimport org.springframework.session.data.mongo.config.annotation.web.http.EnableMongoHttpSession;\n\n@Configuration\n@EnableMongoHttpSession // 自动化配置 Spring Session 使用 MongoDB 作为数据源\npublic class SessionConfiguration {\n\n    @Bean\n    public AbstractMongoSessionConverter mongoSessionConverter() {\n        return new JacksonMongoSessionConverter();\n    }\n\n}\n"
  },
  {
    "path": "lab-26/lab-26-distributed-session-02/src/main/java/cn/iocoder/springboot/lab26/distributedsession/controller/SessionController.java",
    "content": "package cn.iocoder.springboot.lab26.distributedsession.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport javax.servlet.http.HttpSession;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/session\")\npublic class SessionController {\n\n    @GetMapping(\"/set\") // 其实 PostMapping 更合适，单纯为了方便\n    public void set(HttpSession session,\n                    @RequestParam(\"key\") String key,\n                    @RequestParam(\"value\") String value) {\n        session.setAttribute(key, value);\n    }\n\n    @GetMapping(\"/get_all\")\n    public Map<String, Object> getAll(HttpSession session) {\n        Map<String, Object> result = new HashMap<>();\n        // 遍历\n        for (Enumeration<String> enumeration = session.getAttributeNames();\n             enumeration.hasMoreElements();) {\n            String key = enumeration.nextElement();\n            Object value = session.getAttribute(key);\n            result.put(key, value);\n        }\n        // 返回\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "lab-26/lab-26-distributed-session-02/src/main/resources/application.yaml",
    "content": "spring:\n  data:\n    # MongoDB 配置项，对应 MongoProperties 类\n    mongodb:\n      host: 127.0.0.1\n      port: 27017\n      database: yourdatabase\n      username: test01\n      password: password01\n      # 上述属性，也可以只配置 uri\n\nlogging:\n  level:\n    org:\n      springframework:\n        data:\n          mongodb:\n            core: DEBUG # 打印 mongodb 操作的具体语句。生产环境下，不建议开启。\n"
  },
  {
    "path": "lab-26/lab-26-distributed-session-springsecurity/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.10.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-26-distributed-session-springsecurity</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Session 使用 Redis 作为数据源的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.session</groupId>\n            <artifactId>spring-session-data-redis</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Data Redis 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis</artifactId>\n            <exclusions>\n                <!-- 去掉对 Lettuce 的依赖，因为 Spring Boot 优先使用 Lettuce 作为 Redis 客户端 -->\n                <exclusion>\n                    <groupId>io.lettuce</groupId>\n                    <artifactId>lettuce-core</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n        <!-- 引入 Jedis 的依赖，这样 Spring Boot 实现对 Jedis 的自动化配置 -->\n        <dependency>\n            <groupId>redis.clients</groupId>\n            <artifactId>jedis</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-26/lab-26-distributed-session-springsecurity/src/main/java/cn/iocoder/springboot/lab26/distributedsession/Application.java",
    "content": "package cn.iocoder.springboot.lab26.distributedsession;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-26/lab-26-distributed-session-springsecurity/src/main/java/cn/iocoder/springboot/lab26/distributedsession/config/SessionConfiguration.java",
    "content": "package cn.iocoder.springboot.lab26.distributedsession.config;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;\n\n@Configuration\n@EnableRedisHttpSession // 自动化配置 Spring Session 使用 Redis 作为数据源\npublic class SessionConfiguration {\n\n}\n"
  },
  {
    "path": "lab-26/lab-26-distributed-session-springsecurity/src/main/java/cn/iocoder/springboot/lab26/distributedsession/controller/SessionController.java",
    "content": "package cn.iocoder.springboot.lab26.distributedsession.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.session.FindByIndexNameSessionRepository;\nimport org.springframework.session.Session;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/session\")\npublic class SessionController {\n\n    @Autowired\n    private FindByIndexNameSessionRepository sessionRepository;\n\n    @GetMapping(\"/list\")\n    public Map<String, ? extends Session> list(@RequestParam(\"username\") String username) {\n        return sessionRepository.findByPrincipalName(username);\n    }\n\n//    @GetMapping(\"/set\") // 其实 PostMapping 更合适，单纯为了方便\n//    public void set(HttpSession session,\n//                    @RequestParam(\"key\") String key,\n//                    @RequestParam(\"value\") String value) {\n//        session.setAttribute(key, value);\n//    }\n//\n//    @GetMapping(\"/get_all\")\n//    public Map<String, Object> getAll(HttpSession session) {\n//        Map<String, Object> result = new HashMap<>();\n//        // 遍历\n//        for (Enumeration<String> enumeration = session.getAttributeNames();\n//             enumeration.hasMoreElements();) {\n//            String key = enumeration.nextElement();\n//            Object value = session.getAttribute(key);\n//            result.put(key, value);\n//        }\n//        // 返回\n//        return result;\n//    }\n\n}\n"
  },
  {
    "path": "lab-26/lab-26-distributed-session-springsecurity/src/main/resources/application.yaml",
    "content": "spring:\n  # 对应 RedisProperties 类\n  redis:\n    host: 127.0.0.1\n    port: 6379\n    password: # Redis 服务器密码，默认为空。生产中，一定要设置 Redis 密码！\n    database: 0 # Redis 数据库号，默认为 0 。\n    timeout: 0 # Redis 连接超时时间，单位：毫秒。\n    # 对应 RedisProperties.Jedis 内部类\n    jedis:\n      pool:\n        max-active: 8 # 连接池最大连接数，默认为 8 。使用负数表示没有限制。\n        max-idle: 8 # 默认连接数最大空闲的连接数，默认为 8 。使用负数表示没有限制。\n        min-idle: 0 # 默认连接池最小空闲的连接数，默认为 0 。允许设置 0 和 正数。\n        max-wait: -1 # 连接池最大阻塞等待时间，单位：毫秒。默认为 -1 ，表示不限制。\n  # 对应 SecurityProperties 类\n  security:\n    user: # 配置内存中，可登陆的用户名和密码\n      name: yudaoyuanma\n      password: nicai\n"
  },
  {
    "path": "lab-26/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-26</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-26-distributed-session-01</module>\n        <module>lab-26-distributed-session-02</module>\n        <module>lab-26-distributed-session-springsecurity</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-26/《芋道 Spring Boot 分布式 Session 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Distributed-Session/?github>\n"
  },
  {
    "path": "lab-27/lab-27-webflux-01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-27-webflux-01</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring WebFlux 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n            <version>2.2.1.RELEASE</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-27/lab-27-webflux-01/src/main/java/cn/iocoder/springboot/lab27/springwebflux/Application.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-01/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.controller;\n\nimport cn.iocoder.springboot.lab27.springwebflux.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab27.springwebflux.dto.UserUpdateDTO;\nimport cn.iocoder.springboot.lab27.springwebflux.service.UserService;\nimport cn.iocoder.springboot.lab27.springwebflux.vo.UserVO;\nimport org.reactivestreams.Publisher;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 用户 Controller\n */\n@RestController\n@RequestMapping(\"/users\")\npublic class UserController {\n\n    @Autowired\n    private UserService userService;\n\n    /**\n     * 查询用户列表\n     *\n     * @return 用户列表\n     */\n    @GetMapping(\"/list\")\n    public Flux<UserVO> list() {\n        // 查询列表\n        List<UserVO> result = new ArrayList<>();\n        result.add(new UserVO().setId(1).setUsername(\"yudaoyuanma\"));\n        result.add(new UserVO().setId(2).setUsername(\"woshiyutou\"));\n        result.add(new UserVO().setId(3).setUsername(\"chifanshuijiao\"));\n        // 返回列表\n        return Flux.fromIterable(result);\n    }\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @GetMapping(\"/get\")\n    public Mono<UserVO> get(@RequestParam(\"id\") Integer id) {\n        // 查询用户\n        UserVO user = new UserVO().setId(id).setUsername(\"username:\" + id);\n        // 返回\n        return Mono.just(user);\n    }\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @GetMapping(\"/v2/get\")\n    public Mono<UserVO> get2(@RequestParam(\"id\") Integer id) {\n        // 查询用户\n        UserVO user = userService.get(id);\n        // 返回\n        return Mono.just(user);\n    }\n\n    /**\n     * 添加用户\n     *\n     * @param addDTO 添加用户信息 DTO\n     * @return 添加成功的用户编号\n     */\n    @PostMapping(\"add\")\n    public Mono<Integer> add(@RequestBody Publisher<UserAddDTO> addDTO) {\n        // 插入用户记录，返回编号\n        Integer returnId = 1;\n        // 返回用户编号\n        return Mono.just(returnId);\n    }\n\n    /**\n     * 添加用户\n     *\n     * @param addDTO 添加用户信息 DTO\n     * @return 添加成功的用户编号\n     */\n    @PostMapping(\"add2\")\n    public Mono<Integer> add2(Mono<UserAddDTO> addDTO) {\n        // 插入用户记录，返回编号\n        Integer returnId = 1;\n        // 返回用户编号\n        return Mono.just(returnId);\n    }\n\n    /**\n     * 更新指定用户编号的用户\n     *\n     * @param updateDTO 更新用户信息 DTO\n     * @return 是否修改成功\n     */\n    @PostMapping(\"/update\")\n    public Mono<Boolean> update(@RequestBody Publisher<UserUpdateDTO> updateDTO) {\n        // 更新用户记录\n        Boolean success = true;\n        // 返回更新是否成功\n        return Mono.just(success);\n    }\n\n    /**\n     * 删除指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 是否删除成功\n     */\n    @PostMapping(\"/delete\") // URL 修改成 /delete ，RequestMethod 改成 DELETE\n    public Mono<Boolean> delete(@RequestParam(\"id\") Integer id) {\n        // 删除用户记录\n        Boolean success = true;\n        // 返回是否更新成功\n        return Mono.just(success);\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-01/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserRouter.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.controller;\n\nimport cn.iocoder.springboot.lab27.springwebflux.vo.UserVO;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.reactive.function.server.*;\nimport reactor.core.publisher.Mono;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\n\nimport static org.springframework.web.reactive.function.server.RequestPredicates.*;\nimport static org.springframework.web.reactive.function.server.RouterFunctions.*;\nimport static org.springframework.web.reactive.function.server.ServerResponse.*;\n\n/**\n * 用户 Router\n */\n@Configuration\npublic class UserRouter {\n\n    @Bean\n    public RouterFunction<ServerResponse> userListRouterFunction() {\n        return RouterFunctions.route(RequestPredicates.GET(\"/users2/list\"),\n                new HandlerFunction<ServerResponse>() {\n\n                    @Override\n                    public Mono<ServerResponse> handle(ServerRequest request) {\n                        // 查询列表\n                        List<UserVO> result = new ArrayList<>();\n                        result.add(new UserVO().setId(1).setUsername(\"yudaoyuanma\"));\n                        result.add(new UserVO().setId(2).setUsername(\"woshiyutou\"));\n                        result.add(new UserVO().setId(3).setUsername(\"chifanshuijiao\"));\n                        // 返回列表\n                        return ServerResponse.ok().bodyValue(result);\n                    }\n\n                });\n    }\n\n    @Bean\n    public RouterFunction<ServerResponse> userGetRouterFunction() {\n        return RouterFunctions.route(RequestPredicates.GET(\"/users2/get\"),\n                new HandlerFunction<ServerResponse>() {\n\n                    @Override\n                    public Mono<ServerResponse> handle(ServerRequest request) {\n                        // 获得编号\n                        Integer id = request.queryParam(\"id\")\n                                .map(s -> StringUtils.isEmpty(s) ? null : Integer.valueOf(s)).get();\n                        // 查询用户\n                        UserVO user = new UserVO().setId(id).setUsername(UUID.randomUUID().toString());\n                        // 返回列表\n                        return ServerResponse.ok().bodyValue(user);\n                    }\n\n                });\n    }\n\n    @Bean\n    public RouterFunction<ServerResponse> demoRouterFunction() {\n        return route(GET(\"/users2/demo\"), request -> ok().bodyValue(\"demo\"));\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-01/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.dto;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO {\n\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserAddDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserAddDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-01/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dto/UserUpdateDTO.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.dto;\n\npublic class UserUpdateDTO {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserUpdateDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserUpdateDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserUpdateDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-01/src/main/java/cn/iocoder/springboot/lab27/springwebflux/service/UserService.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.service;\n\nimport cn.iocoder.springboot.lab27.springwebflux.vo.UserVO;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class UserService {\n\n    public UserVO get(Integer id) {\n        return new UserVO().setId(id).setUsername(\"test\");\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-01/src/main/java/cn/iocoder/springboot/lab27/springwebflux/vo/UserVO.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.vo;\n\n/**\n * 用户 VO\n */\npublic class UserVO {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserVO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserVO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-01/src/test/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserControllerTest.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.controller;\n\nimport cn.iocoder.springboot.lab27.springwebflux.Application;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.test.web.reactive.server.EntityExchangeResult;\nimport org.springframework.test.web.reactive.server.WebTestClient;\nimport org.springframework.web.reactive.function.BodyInserters;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.function.Consumer;\n\n/**\n * UserController 集成测试\n */\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\n@AutoConfigureWebTestClient\npublic class UserControllerTest {\n\n    @Autowired\n    private WebTestClient webClient;\n\n    @Test\n    public void testList() {\n        webClient.get().uri(\"/users/list\")\n                .exchange() // 执行请求\n                .expectStatus().isOk() // 响应状态码 200\n                .expectBody().json(\"[\\n\" +\n                \"    {\\n\" +\n                \"        \\\"id\\\": 1,\\n\" +\n                \"        \\\"username\\\": \\\"yudaoyuanma\\\"\\n\" +\n                \"    },\\n\" +\n                \"    {\\n\" +\n                \"        \\\"id\\\": 2,\\n\" +\n                \"        \\\"username\\\": \\\"woshiyutou\\\"\\n\" +\n                \"    },\\n\" +\n                \"    {\\n\" +\n                \"        \\\"id\\\": 3,\\n\" +\n                \"        \\\"username\\\": \\\"chifanshuijiao\\\"\\n\" +\n                \"    }\\n\" +\n                \"]\"); // 响应结果\n    }\n\n    @Test\n    public void testGet() {\n        // 获得指定用户编号的用户\n        webClient.get().uri(\"/users/get?id=1\")\n                .exchange() // 执行请求\n                .expectStatus().isOk() // 响应状态码 200\n                .expectBody().json(\"{\\n\" +\n                \"    \\\"id\\\": 1,\\n\" +\n                \"    \\\"username\\\": \\\"username:1\\\"\\n\" +\n                \"}\"); // 响应结果\n    }\n\n    @Test\n    public void testGet2() {\n        // 获得指定用户编号的用户\n        webClient.get().uri(\"/users/v2/get?id=1\")\n                .exchange() // 执行请求\n                .expectStatus().isOk() // 响应状态码 200\n                .expectBody().json(\"{\\n\" +\n                \"    \\\"id\\\": 1,\\n\" +\n                \"    \\\"username\\\": \\\"test\\\"\\n\" +\n                \"}\"); // 响应结果\n    }\n\n    @Test\n    public void testAdd() {\n        Map<String, Object> params = new HashMap<>();\n        params.put(\"username\", \"yudaoyuanma\");\n        params.put(\"password\", \"nicai\");\n        // 添加用户\n        webClient.post().uri(\"/users/add\")\n                .bodyValue(params)\n                .exchange() // 执行请求\n                .expectStatus().isOk() // 响应状态码 200\n                .expectBody().json(\"1\"); // 响应结果。因为没有提供 content 的比较，所以只好使用 json 来比较。竟然能通过\n    }\n\n    @Test\n    public void testAdd2() { // 发送文件的测试，可以参考 https://dev.to/shavz/sending-multipart-form-data-using-spring-webtestclient-2gb7 文章\n        BodyInserters.FormInserter<String> formData = // Form Data 数据，需要这么拼凑\n                BodyInserters.fromFormData(\"username\", \"yudaoyuanma\")\n                .with(\"password\", \"nicai\");\n        // 添加用户\n        webClient.post().uri(\"/users/add2\")\n                .body(formData)\n                .exchange() // 执行请求\n                .expectStatus().isOk() // 响应状态码 200\n                .expectBody().json(\"1\"); // 响应结果。因为没有提供 content 的比较，所以只好使用 json 来比较。竟然能通过\n    }\n\n\n    @Test\n    public void testUpdate() {\n        Map<String, Object> params = new HashMap<>();\n        params.put(\"id\", 1);\n        params.put(\"username\", \"yudaoyuanma\");\n        // 修改用户\n        webClient.post().uri(\"/users/update\")\n                .bodyValue(params)\n                .exchange() // 执行请求\n                .expectStatus().isOk() // 响应状态码 200\n                .expectBody(Boolean.class) // 期望返回值类型是 Boolean\n                .consumeWith((Consumer<EntityExchangeResult<Boolean>>) result -> // 通过消费结果，判断符合是 true 。\n                        Assert.assertTrue(\"返回结果需要为 true\", result.getResponseBody()));\n    }\n\n    @Test\n    public void testDelete() {\n        // 删除用户\n        webClient.post().uri(\"/users/delete?id=1\")\n                .exchange() // 执行请求\n                .expectStatus().isOk() // 响应状态码 200\n                .expectBody(Boolean.class) // 期望返回值类型是 Boolean\n                .isEqualTo(true); // 这样更加简洁一些\n//                .consumeWith((Consumer<EntityExchangeResult<Boolean>>) result -> // 通过消费结果，判断符合是 true 。\n//                        Assert.assertTrue(\"返回结果需要为 true\", result.getResponseBody()));\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-01/src/test/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserControllerTest2.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.controller;\n\nimport cn.iocoder.springboot.lab27.springwebflux.service.UserService;\nimport cn.iocoder.springboot.lab27.springwebflux.vo.UserVO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mockito;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest;\nimport org.springframework.boot.test.mock.mockito.MockBean;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.test.web.reactive.server.WebTestClient;\n\n/**\n * UserController 单元测试\n *\n * 参考 https://howtodoinjava.com/spring-webflux/webfluxtest-with-webtestclient/ 文章\n */\n@RunWith(SpringRunner.class)\n@WebFluxTest(UserController.class)\npublic class UserControllerTest2 {\n\n    @Autowired\n    private WebTestClient webClient;\n\n    @MockBean\n    private UserService userService;\n\n    @Test\n    public void testGet2() throws Exception {\n        // Mock UserService 的 get 方法\n        System.out.println(\"before mock:\" + userService.get(1));\n        Mockito.when(userService.get(1)).thenReturn(\n                new UserVO().setId(1).setUsername(\"username:1\"));\n        System.out.println(\"after mock:\" + userService.get(1));\n\n        // 查询用户列表\n        webClient.get().uri(\"/users/v2/get?id=1\")\n                .exchange() // 执行请求\n                .expectStatus().isOk() // 响应状态码 200\n                .expectBody().json(\"{\\n\" +\n                \"    \\\"id\\\": 1,\\n\" +\n                \"    \\\"username\\\": \\\"username:1\\\"\\n\" +\n                \"}\"); // 响应结果\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-01/src/test/java/cn/iocoder/springboot/lab27/springwebflux/package-info.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux;\n"
  },
  {
    "path": "lab-27/lab-27-webflux-02/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-27-webflux-02</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring WebFlux 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/Application.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/config/WebFluxConfiguration.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.config;\n\nimport cn.iocoder.springboot.lab27.springwebflux.core.web.GlobalResponseBodyHandler;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.http.codec.ServerCodecConfigurer;\nimport org.springframework.web.cors.CorsConfiguration;\nimport org.springframework.web.cors.reactive.CorsWebFilter;\nimport org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;\nimport org.springframework.web.reactive.accept.RequestedContentTypeResolver;\nimport org.springframework.web.reactive.config.WebFluxConfigurer;\n\nimport java.util.Collections;\n\n@Configuration\npublic class WebFluxConfiguration implements WebFluxConfigurer {\n\n    @Bean\n    public GlobalResponseBodyHandler responseWrapper(ServerCodecConfigurer serverCodecConfigurer,\n                                                     RequestedContentTypeResolver requestedContentTypeResolver) {\n        return new GlobalResponseBodyHandler(serverCodecConfigurer.getWriters(), requestedContentTypeResolver);\n    }\n\n//    @Override\n//    public void addCorsMappings(CorsRegistry registry) {\n//        // 添加全局的 CORS 配置\n//        registry.addMapping(\"/**\") // 匹配所有 URL ，相当于全局配置\n//                .allowedOrigins(\"*\") // 允许所有请求来源\n//                .allowCredentials(true) // 允许发送 Cookie\n//                .allowedMethods(\"*\") // 允许所有请求 Method\n//                .allowedHeaders(\"*\") // 允许所有请求 Header\n////                .exposedHeaders(\"*\") // 允许所有响应 Header\n//                .maxAge(1800L); // 有效期 1800 秒，2 小时\n//    }\n\n    @Bean\n    @Order(0) // 设置 order 排序。这个顺序很重要哦，为避免麻烦请设置在最前\n    public CorsWebFilter corsFilter() {\n        // 创建 UrlBasedCorsConfigurationSource 配置源，类似 CorsRegistry 注册表\n        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();\n        // 创建 CorsConfiguration 配置，相当于 CorsRegistration 注册信息\n        CorsConfiguration config = new CorsConfiguration();\n        config.setAllowedOrigins(Collections.singletonList(\"*\")); // 允许所有请求来源\n        config.setAllowCredentials(true); // 允许发送 Cookie\n        config.addAllowedMethod(\"*\"); // 允许所有请求 Method\n        config.setAllowedHeaders(Collections.singletonList(\"*\")); // 允许所有请求 Header\n        // config.setExposedHeaders(Collections.singletonList(\"*\")); // 允许所有响应 Header\n        config.setMaxAge(1800L); // 有效期 1800 秒，2 小时\n        source.registerCorsConfiguration(\"/**\", config);\n        // 创建 CorsWebFilter 对象\n        return new CorsWebFilter(source); // 创建 CorsFilter 过滤器\n    }\n\n\n//    @Override\n//    public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {\n//        configurer.registerDefaults(false);\n//        configurer.customCodecs().decoder(new Jaxb2XmlDecoder());   // <- here\n//        configurer.customCodecs().encoder(new Jaxb2XmlEncoder());   // <- here\n//    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/constants/ServiceExceptionEnum.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.constants;\n\n/**\n * 业务异常枚举\n */\npublic enum ServiceExceptionEnum {\n\n    // ========== 系统级别 ==========\n    SUCCESS(0, \"成功\"),\n    SYS_ERROR(2001001000, \"服务端发生异常\"),\n    MISSING_REQUEST_PARAM_ERROR(2001001001, \"参数缺失\"),\n\n    // ========== 用户模块 ==========\n    USER_NOT_FOUND(1001002000, \"用户不存在\"),\n\n    // ========== 订单模块 ==========\n\n    // ========== 商品模块 ==========\n    ;\n\n    /**\n     * 错误码\n     */\n    private final int code;\n    /**\n     * 错误提示\n     */\n    private final String message;\n\n    ServiceExceptionEnum(int code, String message) {\n        this.code = code;\n        this.message = message;\n    }\n\n    public int getCode() {\n        return code;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.controller;\n\nimport cn.iocoder.springboot.lab27.springwebflux.constants.ServiceExceptionEnum;\nimport cn.iocoder.springboot.lab27.springwebflux.core.exception.ServiceException;\nimport cn.iocoder.springboot.lab27.springwebflux.core.vo.CommonResult;\nimport cn.iocoder.springboot.lab27.springwebflux.vo.UserVO;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.*;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 用户 Controller\n */\n@RestController\n@RequestMapping(\"/users\")\n//@CrossOrigin(value = \"*\")\npublic class UserController {\n\n    /**\n     * 查询用户列表\n     *\n     * @return 用户列表\n     */\n    @GetMapping(\"/list\")\n    public Flux<UserVO> list() {\n        // 查询列表\n        List<UserVO> result = new ArrayList<>();\n        result.add(new UserVO().setId(1).setUsername(\"yudaoyuanma\"));\n        result.add(new UserVO().setId(2).setUsername(\"woshiyutou\"));\n        result.add(new UserVO().setId(3).setUsername(\"chifanshuijiao\"));\n        // 返回列表\n        return Flux.fromIterable(result);\n    }\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @GetMapping(\"/get\")\n//    @PostMapping(\"/get\")\n    public Mono<UserVO> get(@RequestParam(\"id\") Integer id) {\n        // 查询用户\n        UserVO user = new UserVO().setId(id).setUsername(\"username:\" + id);\n        // 返回\n        return Mono.just(user);\n    }\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @GetMapping(\"/get2\")\n    public Mono<CommonResult<UserVO>> get2(@RequestParam(\"id\") Integer id) {\n        // 查询用户\n        UserVO user = new UserVO().setId(id).setUsername(\"username:\" + id);\n        // 返回\n        return Mono.just(CommonResult.success(user));\n    }\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @GetMapping(\"/get3\")\n    public UserVO get3(@RequestParam(\"id\") Integer id) {\n        // 查询用户\n        UserVO user = new UserVO().setId(id).setUsername(\"username:\" + id);\n        // 返回\n        return user;\n    }\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @GetMapping(\"/get4\")\n    public CommonResult<UserVO> get4(@RequestParam(\"id\") Integer id) {\n        // 查询用户\n        UserVO user = new UserVO().setId(id).setUsername(\"username:\" + id);\n        // 返回\n        return CommonResult.success(user);\n    }\n\n    /**\n     * 测试抛出 NullPointerException 异常\n     */\n    @GetMapping(\"/exception-01\")\n    public UserVO exception01() {\n        throw new NullPointerException(\"没有粗面鱼丸\");\n    }\n\n    /**\n     * 测试抛出 ServiceException 异常\n     */\n    @GetMapping(\"/exception-02\")\n    public UserVO exception02() {\n        throw new ServiceException(ServiceExceptionEnum.USER_NOT_FOUND);\n    }\n\n//    @PostMapping(value = \"/add\",\n//            // ↓ 增加 \"application/xml\"、\"application/json\" ，针对 Content-Type 请求头\n//            consumes = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE},\n//            // ↓ 增加 \"application/xml\"、\"application/json\" ，针对 Accept 请求头\n//            produces = {MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE}\n//    )\n\n    @PostMapping(value = \"/add\",\n            // ↓ 增加 \"application/xml\"、\"application/json\" ，针对 Content-Type 请求头\n            consumes = {MediaType.APPLICATION_XML_VALUE},\n            // ↓ 增加 \"application/xml\"、\"application/json\" ，针对 Accept 请求头\n            produces = {MediaType.APPLICATION_XML_VALUE}\n    )\n//    @PostMapping(value = \"/add\")\n    public Mono<UserVO> add(@RequestBody Mono<UserVO> user) {\n        return user;\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserRouter.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.controller;\n\nimport cn.iocoder.springboot.lab27.springwebflux.vo.UserVO;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.reactive.function.server.*;\nimport reactor.core.publisher.Mono;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.UUID;\nimport java.util.function.Consumer;\n\nimport static org.springframework.web.reactive.function.server.RequestPredicates.GET;\nimport static org.springframework.web.reactive.function.server.RouterFunctions.route;\nimport static org.springframework.web.reactive.function.server.ServerResponse.ok;\n\n/**\n * 用户 Router\n */\n@Configuration\npublic class UserRouter {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Bean\n    public RouterFunction<ServerResponse> userListRouterFunction() {\n        return RouterFunctions.route(RequestPredicates.GET(\"/users2/list\"),\n                new HandlerFunction<ServerResponse>() {\n\n                    @Override\n                    public Mono<ServerResponse> handle(ServerRequest request) {\n                        // 查询列表\n                        List<UserVO> result = new ArrayList<>();\n                        result.add(new UserVO().setId(1).setUsername(\"yudaoyuanma\"));\n                        result.add(new UserVO().setId(2).setUsername(\"woshiyutou\"));\n                        result.add(new UserVO().setId(3).setUsername(\"chifanshuijiao\"));\n                        // 返回列表\n                        return ServerResponse.ok().bodyValue(result);\n                    }\n\n                });\n    }\n\n    @Bean\n    public RouterFunction<ServerResponse> userGetRouterFunction() {\n        return RouterFunctions.route(RequestPredicates.GET(\"/users2/get\"),\n                new HandlerFunction<ServerResponse>() {\n\n                    @Override\n                    public Mono<ServerResponse> handle(ServerRequest request) {\n                        // 获得编号\n                        Integer id = request.queryParam(\"id\")\n                                .map(s -> StringUtils.isEmpty(s) ? null : Integer.valueOf(s)).get();\n                        // 查询用户\n                        UserVO user = new UserVO().setId(id).setUsername(UUID.randomUUID().toString());\n                        // 返回列表\n                        return ServerResponse.ok().bodyValue(user);\n                    }\n\n                });\n    }\n\n    @Bean\n    public RouterFunction<ServerResponse> demoRouterFunction() {\n        return route(GET(\"/users2/demo\"), request -> ok().bodyValue(\"demo\"));\n    }\n\n    @Bean\n    public RouterFunction<ServerResponse> demo2RouterFunction() {\n        return route(GET(\"/users2/demo2\"), request -> ok().bodyValue(\"demo\"))\n                .filter(new HandlerFilterFunction<ServerResponse, ServerResponse>() {\n\n                    @Override\n                    public Mono<ServerResponse> filter(ServerRequest request, HandlerFunction<ServerResponse> next) {\n                        return next.handle(request).doOnSuccess(new Consumer<ServerResponse>() { // 执行成功后回调\n\n                            @Override\n                            public void accept(ServerResponse serverResponse) {\n                                logger.info(\"[accept][执行成功]\");\n                            }\n\n                        });\n                    }\n\n                });\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller2/TestController.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.controller2;\n\nimport cn.iocoder.springboot.lab27.springwebflux.vo.UserVO;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport reactor.core.publisher.Mono;\n\nimport java.util.UUID;\n\n/**\n * 测试 Controller\n */\n@RestController\n@RequestMapping(\"/test\")\n//@CrossOrigin(origins = \"*\", allowCredentials = \"true\") // 允许所有来源，允许发送 Cookie\npublic class TestController {\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * @return 用户\n     */\n    @GetMapping(\"/get\")\n//    @CrossOrigin(allowCredentials = \"false\") // 允许所有来源，不允许发送 Cookie\n    public Mono<UserVO> get() {\n        // 查询用户\n        UserVO user =  new UserVO().setId(1).setUsername(UUID.randomUUID().toString());\n        // 返回\n        return Mono.just(user);\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/exception/ServiceException.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.core.exception;\n\nimport cn.iocoder.springboot.lab27.springwebflux.constants.ServiceExceptionEnum;\n\n/**\n * 服务异常\n *\n * 参考 https://www.kancloud.cn/onebase/ob/484204 文章\n *\n * 一共 10 位，分成四段\n *\n * 第一段，1 位，类型\n *      1 - 业务级别异常\n *      2 - 系统级别异常\n * 第二段，3 位，系统类型\n *      001 - 用户系统\n *      002 - 商品系统\n *      003 - 订单系统\n *      004 - 支付系统\n *      005 - 优惠劵系统\n *      ... - ...\n * 第三段，3 位，模块\n *      不限制规则。\n *      一般建议，每个系统里面，可能有多个模块，可以再去做分段。以用户系统为例子：\n *          001 - OAuth2 模块\n *          002 - User 模块\n *          003 - MobileCode 模块\n * 第四段，3 位，错误码\n *       不限制规则。\n *       一般建议，每个模块自增。\n */\npublic final class ServiceException extends RuntimeException {\n\n    /**\n     * 错误码\n     */\n    private final Integer code;\n\n    public ServiceException(ServiceExceptionEnum serviceExceptionEnum) {\n        // 使用父类的 message 字段\n        super(serviceExceptionEnum.getMessage());\n        // 设置错误码\n        this.code = serviceExceptionEnum.getCode();\n    }\n\n    public Integer getCode() {\n        return code;\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/filter/DemoWebFilter.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.core.filter;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.server.ServerWebExchange;\nimport org.springframework.web.server.WebFilter;\nimport org.springframework.web.server.WebFilterChain;\nimport reactor.core.publisher.Mono;\n\nimport java.util.function.Consumer;\n\n@Component\n@Order(1)\npublic class DemoWebFilter implements WebFilter {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {\n        // 继续执行请求\n        return webFilterChain.filter(serverWebExchange)\n                .doOnSuccess(new Consumer<Void>() { // 执行成功后回调\n\n                    @Override\n                    public void accept(Void aVoid) {\n                        logger.info(\"[accept][执行成功]\");\n                    }\n\n                });\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/package-info.java",
    "content": "/**\n * 提供核心封装\n */\npackage cn.iocoder.springboot.lab27.springwebflux.core;\n"
  },
  {
    "path": "lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/vo/CommonResult.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.core.vo;\n\nimport com.fasterxml.jackson.annotation.JsonIgnore;\nimport org.springframework.util.Assert;\n\nimport javax.xml.bind.annotation.XmlRootElement;\nimport java.io.Serializable;\n\n/**\n * 通用返回结果\n *\n * @param <T> 结果泛型\n */\n@XmlRootElement\npublic class CommonResult<T> implements Serializable {\n\n    public static Integer CODE_SUCCESS = 0;\n\n    /**\n     * 错误码\n     */\n    private Integer code;\n    /**\n     * 错误提示\n     */\n    private String message;\n    /**\n     * 返回数据\n     */\n    private T data;\n\n    /**\n     * 将传入的 result 对象，转换成另外一个泛型结果的对象\n     *\n     * 因为 A 方法返回的 CommonResult 对象，不满足调用其的 B 方法的返回，所以需要进行转换。\n     *\n     * @param result 传入的 result 对象\n     * @param <T> 返回的泛型\n     * @return 新的 CommonResult 对象\n     */\n    public static <T> CommonResult<T> error(CommonResult<?> result) {\n        return error(result.getCode(), result.getMessage());\n    }\n\n    public static <T> CommonResult<T> error(Integer code, String message) {\n        Assert.isTrue(!CODE_SUCCESS.equals(code), \"code 必须是错误的！\");\n        CommonResult<T> result = new CommonResult<>();\n        result.code = code;\n        result.message = message;\n        return result;\n    }\n\n    public static <T> CommonResult<T> success(T data) {\n        CommonResult<T> result = new CommonResult<>();\n        result.code = CODE_SUCCESS;\n        result.data = data;\n        result.message = \"\";\n        return result;\n    }\n\n    public Integer getCode() {\n        return code;\n    }\n\n    public void setCode(Integer code) {\n        this.code = code;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public void setMessage(String message) {\n        this.message = message;\n    }\n\n    public T getData() {\n        return data;\n    }\n\n    public void setData(T data) {\n        this.data = data;\n    }\n\n    @JsonIgnore\n    public boolean isSuccess() {\n        return CODE_SUCCESS.equals(code);\n    }\n\n    @JsonIgnore\n    public boolean isError() {\n        return !isSuccess();\n    }\n\n    @Override\n    public String toString() {\n        return \"CommonResult{\" +\n                \"code=\" + code +\n                \", message='\" + message + '\\'' +\n                \", data=\" + data +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/web/GlobalExceptionHandler.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.core.web;\n\nimport cn.iocoder.springboot.lab27.springwebflux.constants.ServiceExceptionEnum;\nimport cn.iocoder.springboot.lab27.springwebflux.core.exception.ServiceException;\nimport cn.iocoder.springboot.lab27.springwebflux.core.vo.CommonResult;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.server.ServerWebInputException;\n\n@ControllerAdvice(basePackages = \"cn.iocoder.springboot.lab27.springwebflux.controller\")\npublic class GlobalExceptionHandler {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    /**\n     * 处理 ServiceException 异常\n     */\n    @ResponseBody\n    @ExceptionHandler(value = ServiceException.class)\n    public CommonResult serviceExceptionHandler(ServiceException ex) {\n        logger.debug(\"[serviceExceptionHandler]\", ex);\n        // 包装 CommonResult 结果\n        return CommonResult.error(ex.getCode(), ex.getMessage());\n    }\n\n    /**\n     * 处理 ServerWebInputException 异常\n     *\n     * WebFlux 参数不正确\n     */\n    @ResponseBody\n    @ExceptionHandler(value = ServerWebInputException.class)\n    public CommonResult serverWebInputExceptionHandler(ServerWebInputException ex) {\n        logger.debug(\"[ServerWebInputExceptionHandler]\", ex);\n        // 包装 CommonResult 结果\n        return CommonResult.error(ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getCode(),\n                ServiceExceptionEnum.MISSING_REQUEST_PARAM_ERROR.getMessage());\n    }\n\n    /**\n     * 处理其它 Exception 异常\n     */\n    @ResponseBody\n    @ExceptionHandler(value = Exception.class)\n    public CommonResult exceptionHandler(Exception e) {\n        // 记录异常日志\n        logger.error(\"[exceptionHandler]\", e);\n        // 返回 ERROR CommonResult\n        return CommonResult.error(ServiceExceptionEnum.SYS_ERROR.getCode(),\n                ServiceExceptionEnum.SYS_ERROR.getMessage());\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/web/GlobalResponseBodyHandler.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.core.web;\n\nimport cn.iocoder.springboot.lab27.springwebflux.core.vo.CommonResult;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.core.MethodParameter;\nimport org.springframework.core.ReactiveAdapterRegistry;\nimport org.springframework.http.codec.HttpMessageWriter;\nimport org.springframework.web.reactive.HandlerResult;\nimport org.springframework.web.reactive.accept.RequestedContentTypeResolver;\nimport org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler;\nimport org.springframework.web.server.ServerWebExchange;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.util.List;\nimport java.util.function.Function;\n\npublic class GlobalResponseBodyHandler extends ResponseBodyResultHandler {\n\n    private static Logger LOGGER = LoggerFactory.getLogger(GlobalResponseBodyHandler.class);\n\n    private static MethodParameter METHOD_PARAMETER_MONO_COMMON_RESULT;\n\n    private static final CommonResult COMMON_RESULT_SUCCESS = CommonResult.success(null);\n\n    static {\n        try {\n            // 获得 METHOD_PARAMETER_MONO_COMMON_RESULT 。其中 -1 表示 `#methodForParams()` 方法的返回值\n            METHOD_PARAMETER_MONO_COMMON_RESULT = new MethodParameter(\n                    GlobalResponseBodyHandler.class.getDeclaredMethod(\"methodForParams\"), -1);\n        } catch (NoSuchMethodException e) {\n            LOGGER.error(\"[static][获取 METHOD_PARAMETER_MONO_COMMON_RESULT 时，找不都方法\");\n            throw new RuntimeException(e);\n        }\n    }\n\n    public GlobalResponseBodyHandler(List<HttpMessageWriter<?>> writers, RequestedContentTypeResolver resolver) {\n        super(writers, resolver);\n    }\n\n    public GlobalResponseBodyHandler(List<HttpMessageWriter<?>> writers, RequestedContentTypeResolver resolver, ReactiveAdapterRegistry registry) {\n        super(writers, resolver, registry);\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public Mono<Void> handleResult(ServerWebExchange exchange, HandlerResult result) {\n        Object returnValue = result.getReturnValue();\n        Object body;\n        // 处理返回结果为 Mono 的情况\n        if (returnValue instanceof Mono) {\n            body = ((Mono<Object>) result.getReturnValue())\n                    .map((Function<Object, Object>) GlobalResponseBodyHandler::wrapCommonResult)\n                    .defaultIfEmpty(COMMON_RESULT_SUCCESS);\n        // 处理返回结果为 Flux 的情况\n        } else if (returnValue instanceof Flux) {\n            body = ((Flux<Object>) result.getReturnValue())\n                    .collectList()\n                    .map((Function<Object, Object>) GlobalResponseBodyHandler::wrapCommonResult)\n                    .defaultIfEmpty(COMMON_RESULT_SUCCESS);\n        // 处理结果为其它类型\n        } else {\n            body = wrapCommonResult(returnValue);\n        }\n        return writeBody(body, METHOD_PARAMETER_MONO_COMMON_RESULT, exchange);\n    }\n\n    private static Mono<CommonResult> methodForParams() {\n        return null;\n    }\n\n    private static CommonResult<?> wrapCommonResult(Object body) {\n        // 如果已经是 CommonResult 类型，则直接返回\n        if (body instanceof CommonResult) {\n            return (CommonResult<?>) body;\n        }\n        // 如果不是，则包装成 CommonResult 类型\n        return CommonResult.success(body);\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-02/src/main/java/cn/iocoder/springboot/lab27/springwebflux/vo/UserVO.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.vo;\n\nimport javax.xml.bind.annotation.XmlRootElement;\n\n/**\n * 用户 VO\n */\n@XmlRootElement\npublic class UserVO {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserVO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserVO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-03/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-27-webflux-03</artifactId>\n    <description>测试在 WebFlux 中，使用 Java Servlet 相关的 Servlet、Filter、Listener</description>\n\n    <dependencies>\n        <!-- 实现对 Spring WebFlux 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n            <exclusions>\n                <exclusion>\n                    <groupId>org.springframework.boot</groupId>\n                    <artifactId>spring-boot-starter-netty</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-tomcat</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-27/lab-27-webflux-03/src/main/java/cn/iocoder/springboot/lab27/springwebflux/Application.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.boot.web.servlet.ServletComponentScan;\nimport org.springframework.boot.web.servlet.ServletListenerRegistrationBean;\nimport org.springframework.boot.web.servlet.ServletRegistrationBean;\nimport org.springframework.context.annotation.Bean;\n\nimport javax.servlet.*;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\nimport java.util.Collections;\n\n@SpringBootApplication\n@ServletComponentScan\npublic class Application {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Bean\n    public ServletRegistrationBean testServlet01() {\n        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean<>(new HttpServlet() {\n\n            @Override\n            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n                logger.info(\"[doGet][uri: {}]\", req.getRequestURI());\n            }\n\n        });\n        servletRegistrationBean.setUrlMappings(Collections.singleton(\"/test/01\"));\n        return servletRegistrationBean;\n    }\n\n    @Bean\n    public FilterRegistrationBean testFilter01() {\n        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(new Filter() {\n\n            @Override\n            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {\n                logger.info(\"[doFilter]\");\n                filterChain.doFilter(servletRequest, servletResponse);\n            }\n\n        });\n        filterRegistrationBean.setUrlPatterns(Collections.singleton(\"/test/*\"));\n        return filterRegistrationBean;\n    }\n\n    @Bean\n    public ServletListenerRegistrationBean<?> testListener01() {\n        return new ServletListenerRegistrationBean<>(new ServletContextListener() {\n\n            @Override\n            public void contextInitialized(ServletContextEvent sce) {\n                logger.info(\"[contextInitialized]\");\n            }\n\n            @Override\n            public void contextDestroyed(ServletContextEvent sce) {\n\n            }\n\n        });\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-03/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.controller;\n\nimport cn.iocoder.springboot.lab27.springwebflux.vo.UserVO;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport reactor.core.publisher.Flux;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * 用户 Controller\n */\n@RestController\n@RequestMapping(\"/users\")\npublic class UserController {\n\n    /**\n     * 查询用户列表\n     *\n     * @return 用户列表\n     */\n    @GetMapping(\"/list\")\n    public Flux<UserVO> list() {\n        // 查询列表\n        List<UserVO> result = new ArrayList<>();\n        result.add(new UserVO().setId(1).setUsername(\"yudaoyuanma\"));\n        result.add(new UserVO().setId(2).setUsername(\"woshiyutou\"));\n        result.add(new UserVO().setId(3).setUsername(\"chifanshuijiao\"));\n        // 返回列表\n        return Flux.fromIterable(result);\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-03/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/package-info.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.core;\n"
  },
  {
    "path": "lab-27/lab-27-webflux-03/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/servlet/TestFilter02.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.core.servlet;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.servlet.*;\nimport javax.servlet.annotation.WebFilter;\nimport java.io.IOException;\n\n@WebFilter(\"/users/*\")\npublic class TestFilter02 implements Filter {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {\n        logger.info(\"[doFilter]\");\n        filterChain.doFilter(servletRequest, servletResponse);\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-03/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/servlet/TestServlet02.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.core.servlet;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.servlet.ServletException;\nimport javax.servlet.annotation.WebServlet;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport java.io.IOException;\n\n@WebServlet(urlPatterns = \"/test/02\")\npublic class TestServlet02 extends HttpServlet {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {\n        logger.info(\"[doGet][uri: {}]\", req.getRequestURI());\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-03/src/main/java/cn/iocoder/springboot/lab27/springwebflux/core/servlet/TestServletContextListener02.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.core.servlet;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport javax.servlet.ServletContextEvent;\nimport javax.servlet.ServletContextListener;\nimport javax.servlet.annotation.WebListener;\n\n@WebListener\npublic class TestServletContextListener02 implements ServletContextListener {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void contextInitialized(ServletContextEvent sce) {\n        logger.info(\"[contextInitialized]\");\n    }\n\n    @Override\n    public void contextDestroyed(ServletContextEvent sce) {\n\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-03/src/main/java/cn/iocoder/springboot/lab27/springwebflux/vo/UserVO.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.vo;\n\n/**\n * 用户 VO\n */\npublic class UserVO {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserVO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserVO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-elasticsearch/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-27-webflux-elasticsearch</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring WebFlux 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n            <version>2.2.1.RELEASE</version>\n        </dependency>\n\n        <!-- 自动化配置响应式的 Spring Data Elasticsearch -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-27/lab-27-webflux-elasticsearch/src/main/java/cn/iocoder/springboot/lab27/springwebflux/Application.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n//        System.setProperty(\"es.set.netty.runtime.available.processors\", \"false\");\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-elasticsearch/src/main/java/cn/iocoder/springboot/lab27/springwebflux/config/ElasticsearchConfiguration.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.config;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.elasticsearch.repository.config.EnableReactiveElasticsearchRepositories;\n\n@Configuration\n@EnableReactiveElasticsearchRepositories // 开启响应式的 Elasticsearch 的 Repository 的自动化配置\npublic class ElasticsearchConfiguration {\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-elasticsearch/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.controller;\n\nimport cn.iocoder.springboot.lab27.springwebflux.dao.UserRepository;\nimport cn.iocoder.springboot.lab27.springwebflux.dataobject.UserDO;\nimport cn.iocoder.springboot.lab27.springwebflux.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab27.springwebflux.dto.UserUpdateDTO;\nimport cn.iocoder.springboot.lab27.springwebflux.vo.UserVO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.util.Date;\nimport java.util.Objects;\nimport java.util.function.Function;\n\n/**\n * 用户 Controller\n */\n@RestController\n@RequestMapping(\"/users\")\npublic class UserController {\n\n    private static final UserDO USER_NULL = new UserDO();\n\n    @Autowired\n    private UserRepository userRepository;\n\n\n    /**\n     * 查询用户列表\n     *\n     * @return 用户列表\n     */\n    @GetMapping(\"/list\")\n    public Flux<UserVO> list() {\n        // 返回列表\n        return userRepository.findAll()\n                .map(userDO -> new UserVO().setId(userDO.getId()).setUsername(userDO.getUsername()));\n    }\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @GetMapping(\"/get\")\n    public Mono<UserVO> get(@RequestParam(\"id\") Integer id) {\n        // 返回\n        return userRepository.findById(id)\n                .map(userDO -> new UserVO().setId(userDO.getId()).setUsername(userDO.getUsername()));\n    }\n\n    /**\n     * 添加用户\n     *\n     * @param addDTO 添加用户信息 DTO\n     * @return 添加成功的用户编号\n     */\n    @PostMapping(\"add\")\n    public Mono<Integer> add(UserAddDTO addDTO) {\n        // 查询用户\n        Mono<UserDO> user = userRepository.findByUsername(addDTO.getUsername());\n\n        // 执行插入\n        return user.defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况，否则 flatMap 不会往下走\n                .flatMap(new Function<UserDO, Mono<Integer>>() {\n\n                    @Override\n                    public Mono<Integer> apply(UserDO userDO) {\n                        if (userDO != USER_NULL) {\n                            // 返回 -1 表示插入失败。\n                            // 实际上，一般是抛出 ServiceException 异常。因为这个示例项目里暂时没做全局异常的定义，所以暂时返回 -1 啦\n                            return Mono.just(-1);\n                        }\n                        // 将 addDTO 转成 UserDO\n                        userDO = new UserDO().setId((int) (System.currentTimeMillis() / 1000)) // 使用当前时间戳的描述，作为 ID 。\n                                .setUsername(addDTO.getUsername())\n                                .setPassword(addDTO.getPassword())\n                                .setCreateTime(new Date());\n                        // 插入数据库\n                        return userRepository.save(userDO).map(UserDO::getId);\n                    }\n\n                });\n    }\n\n    /**\n     * 更新指定用户编号的用户\n     *\n     * @param updateDTO 更新用户信息 DTO\n     * @return 是否修改成功\n     */\n    @PostMapping(\"/update\")\n    public Mono<Boolean> update(UserUpdateDTO updateDTO) {\n        // 查询用户\n        Mono<UserDO> user = userRepository.findById(updateDTO.getId());\n\n        // 执行更新\n        return user.defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况，否则 flatMap 不会往下走\n                .flatMap(new Function<UserDO, Mono<Boolean>>() {\n\n                    @Override\n                    public Mono<Boolean> apply(UserDO userDO) {\n                        // 如果不存在该用户，则直接返回 false 失败\n                        if (userDO == USER_NULL) {\n                            return Mono.just(false);\n                        }\n                        // 查询用户是否存在\n                        return userRepository.findByUsername(updateDTO.getUsername())\n                                .defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况，否则 flatMap 不会往下走\n                                .flatMap(new Function<UserDO, Mono<? extends Boolean>>() {\n\n                                    @Override\n                                    public Mono<? extends Boolean> apply(UserDO usernameUserDO) {\n                                        // 如果用户名已经使用（该用户名对应的 id 不是自己，说明就已经被使用了）\n                                        if (usernameUserDO != USER_NULL && !Objects.equals(updateDTO.getId(), usernameUserDO.getId())) {\n                                            return Mono.just(false);\n                                        }\n                                        // 执行更新\n                                        userDO.setUsername(updateDTO.getUsername());\n                                        userDO.setPassword(updateDTO.getPassword());\n                                        return userRepository.save(userDO).map(userDO -> true); // 返回 true 成功\n                                    }\n\n                                });\n                    }\n\n                });\n    }\n\n    /**\n     * 删除指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 是否删除成功\n     */\n    @PostMapping(\"/delete\") // URL 修改成 /delete ，RequestMethod 改成 DELETE\n    public Mono<Boolean> delete(@RequestParam(\"id\") Integer id) {\n        // 查询用户\n        Mono<UserDO> user = userRepository.findById(id);\n\n        // 执行删除。这里仅仅是示例，项目中不要物理删除，而是标记删除\n        return user.defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况，否则 flatMap 不会往下走\n                .flatMap(new Function<UserDO, Mono<Boolean>>() {\n\n                    @Override\n                    public Mono<Boolean> apply(UserDO userDO) {\n                        // 如果不存在该用户，则直接返回 false 失败\n                        if (userDO == USER_NULL) {\n                            return Mono.just(false);\n                        }\n                        // 执行删除\n                        return userRepository.deleteById(id).map(aVoid -> true); // 返回 true 成功\n                    }\n\n                });\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-elasticsearch/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dao/UserRepository.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.dao;\n\nimport cn.iocoder.springboot.lab27.springwebflux.dataobject.UserDO;\nimport org.springframework.data.elasticsearch.repository.ReactiveElasticsearchRepository;\nimport reactor.core.publisher.Mono;\n\npublic interface UserRepository extends ReactiveElasticsearchRepository<UserDO, Integer> {\n\n    Mono<UserDO> findByUsername(String username);\n\n}\n\n//public interface UserRepository extends ElasticsearchRepository<UserDO, Integer> {\n//\n////    Mono<UserDO> findByUsername(String username);\n//\n//}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-elasticsearch/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.dataobject;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.elasticsearch.annotations.Document;\n\nimport java.util.Date;\n\n@Document(indexName = \"user\", // 索引名\n        type = \"user\", // 类型。未来的版本即将废弃\n        shards = 1, // 默认索引分区数\n        replicas = 0, // 每个分区的备份数\n        refreshInterval = \"-1\" // 刷新间隔\n)\npublic class UserDO {\n\n    /**\n     * ID 主键\n     */\n    @Id\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    public UserDO setCreateTime(Date createTime) {\n        this.createTime = createTime;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                \", createTime=\" + createTime +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-elasticsearch/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.dto;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO {\n\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserAddDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserAddDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-elasticsearch/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dto/UserUpdateDTO.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.dto;\n\npublic class UserUpdateDTO {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserUpdateDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserUpdateDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserUpdateDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-elasticsearch/src/main/java/cn/iocoder/springboot/lab27/springwebflux/vo/UserVO.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.vo;\n\n/**\n * 用户 VO\n */\npublic class UserVO {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserVO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserVO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-elasticsearch/src/main/resources/application.yaml",
    "content": "spring:\n  data:\n    # Elasticsearch 配置项\n    elasticsearch:\n      client:\n        # 对应 ReactiveRestClientProperties 配置类\n        reactive:\n          endpoints: 127.0.0.1:9200 # ES Restful API 地址\n"
  },
  {
    "path": "lab-27/lab-27-webflux-mongodb/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-27-webflux-mongodb</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring WebFlux 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n            <version>2.2.1.RELEASE</version>\n        </dependency>\n\n        <!-- 自动化配置响应式的 Spring Data Mongodb -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-27/lab-27-webflux-mongodb/src/main/java/cn/iocoder/springboot/lab27/springwebflux/Application.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-mongodb/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.controller;\n\nimport cn.iocoder.springboot.lab27.springwebflux.dao.UserRepository;\nimport cn.iocoder.springboot.lab27.springwebflux.dataobject.UserDO;\nimport cn.iocoder.springboot.lab27.springwebflux.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab27.springwebflux.dto.UserUpdateDTO;\nimport cn.iocoder.springboot.lab27.springwebflux.vo.UserVO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.util.Date;\nimport java.util.Objects;\nimport java.util.function.Function;\n\n/**\n * 用户 Controller\n */\n@RestController\n@RequestMapping(\"/users\")\npublic class UserController {\n\n    private static final UserDO USER_NULL = new UserDO();\n\n    @Autowired\n    private UserRepository userRepository;\n\n    /**\n     * 查询用户列表\n     *\n     * @return 用户列表\n     */\n    @GetMapping(\"/list\")\n    public Flux<UserVO> list() {\n        // 返回列表\n        return userRepository.findAll()\n                .map(userDO -> new UserVO().setId(userDO.getId()).setUsername(userDO.getUsername()));\n    }\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @GetMapping(\"/get\")\n    public Mono<UserVO> get(@RequestParam(\"id\") Integer id) {\n        // 返回\n        return userRepository.findById(id)\n                .map(userDO -> new UserVO().setId(userDO.getId()).setUsername(userDO.getUsername()));\n    }\n\n    /**\n     * 添加用户\n     *\n     * @param addDTO 添加用户信息 DTO\n     * @return 添加成功的用户编号\n     */\n    @PostMapping(\"add\")\n    public Mono<Integer> add(UserAddDTO addDTO) {\n        // 查询用户\n        Mono<UserDO> user = userRepository.findByUsername(addDTO.getUsername());\n\n        // 执行插入\n        return user.defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况，否则 flatMap 不会往下走\n                .flatMap(new Function<UserDO, Mono<Integer>>() {\n\n                    @Override\n                    public Mono<Integer> apply(UserDO userDO) {\n                        if (userDO != USER_NULL) {\n                            // 返回 -1 表示插入失败。\n                            // 实际上，一般是抛出 ServiceException 异常。因为这个示例项目里暂时没做全局异常的定义，所以暂时返回 -1 啦\n                            return Mono.just(-1);\n                        }\n                        // 将 addDTO 转成 UserDO\n                        userDO = new UserDO().setId((int) (System.currentTimeMillis() / 1000)) // 使用当前时间戳的描述，作为 ID 。\n                                .setUsername(addDTO.getUsername())\n                                .setPassword(addDTO.getPassword())\n                                .setCreateTime(new Date());\n                        // 插入数据库\n                        return userRepository.insert(userDO).map(UserDO::getId);\n                    }\n\n                });\n    }\n\n    /**\n     * 更新指定用户编号的用户\n     *\n     * @param updateDTO 更新用户信息 DTO\n     * @return 是否修改成功\n     */\n    @PostMapping(\"/update\")\n    public Mono<Boolean> update(UserUpdateDTO updateDTO) {\n        // 查询用户\n        Mono<UserDO> user = userRepository.findById(updateDTO.getId());\n\n        // 执行更新\n        return user.defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况，否则 flatMap 不会往下走\n                .flatMap(new Function<UserDO, Mono<Boolean>>() {\n\n                    @Override\n                    public Mono<Boolean> apply(UserDO userDO) {\n                        // 如果不存在该用户，则直接返回 false 失败\n                        if (userDO == USER_NULL) {\n                            return Mono.just(false);\n                        }\n                        // 查询用户是否存在\n                        return userRepository.findByUsername(updateDTO.getUsername())\n                                .defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况，否则 flatMap 不会往下走\n                                .flatMap(new Function<UserDO, Mono<? extends Boolean>>() {\n\n                                    @Override\n                                    public Mono<? extends Boolean> apply(UserDO usernameUserDO) {\n                                        // 如果用户名已经使用（该用户名对应的 id 不是自己，说明就已经被使用了）\n                                        if (usernameUserDO != USER_NULL && !Objects.equals(updateDTO.getId(), usernameUserDO.getId())) {\n                                            return Mono.just(false);\n                                        }\n                                        // 执行更新\n                                        userDO.setUsername(updateDTO.getUsername());\n                                        userDO.setPassword(updateDTO.getPassword());\n                                        return userRepository.save(userDO).map(userDO -> true); // 返回 true 成功\n                                    }\n\n                                });\n                    }\n\n                });\n    }\n\n    /**\n     * 删除指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 是否删除成功\n     */\n    @PostMapping(\"/delete\") // URL 修改成 /delete ，RequestMethod 改成 DELETE\n    public Mono<Boolean> delete(@RequestParam(\"id\") Integer id) {\n        // 查询用户\n        Mono<UserDO> user = userRepository.findById(id);\n\n        // 执行删除。这里仅仅是示例，项目中不要物理删除，而是标记删除\n        return user.defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况，否则 flatMap 不会往下走\n                .flatMap(new Function<UserDO, Mono<Boolean>>() {\n\n                    @Override\n                    public Mono<Boolean> apply(UserDO userDO) {\n                        // 如果不存在该用户，则直接返回 false 失败\n                        if (userDO == USER_NULL) {\n                            return Mono.just(false);\n                        }\n                        // 执行删除\n                        return userRepository.deleteById(id).map(aVoid -> true); // 返回 true 成功\n                    }\n\n                });\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-mongodb/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dao/UserRepository.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.dao;\n\nimport cn.iocoder.springboot.lab27.springwebflux.dataobject.UserDO;\nimport org.springframework.data.mongodb.repository.ReactiveMongoRepository;\nimport reactor.core.publisher.Mono;\n\npublic interface UserRepository extends ReactiveMongoRepository<UserDO, Integer> {\n\n    Mono<UserDO> findByUsername(String username);\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-mongodb/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.dataobject;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.mongodb.core.mapping.Document;\n\nimport java.util.Date;\n\n/**\n * 用户 DO\n */\n@Document(collection = \"User\")\npublic class UserDO {\n\n    @Id\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    public UserDO setCreateTime(Date createTime) {\n        this.createTime = createTime;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                \", createTime=\" + createTime +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-mongodb/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.dto;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO {\n\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserAddDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserAddDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-mongodb/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dto/UserUpdateDTO.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.dto;\n\npublic class UserUpdateDTO {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserUpdateDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserUpdateDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserUpdateDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-mongodb/src/main/java/cn/iocoder/springboot/lab27/springwebflux/vo/UserVO.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.vo;\n\n/**\n * 用户 VO\n */\npublic class UserVO {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserVO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserVO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-mongodb/src/main/resources/application.yaml",
    "content": "spring:\n  data:\n    # MongoDB 配置项，对应 MongoProperties 类\n    mongodb:\n      host: 127.0.0.1\n      port: 27017\n      database: yourdatabase\n      username: test01\n      password: password01\n      # 上述属性，也可以只配置 uri\n\nlogging:\n  level:\n    org:\n      springframework:\n        data:\n          mongodb:\n            core: DEBUG # 打印 mongodb 操作的具体语句。生产环境下，不建议开启。\n"
  },
  {
    "path": "lab-27/lab-27-webflux-r2dbc/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-27-webflux-r2dbc</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring WebFlux 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n            <version>2.2.1.RELEASE</version>\n        </dependency>\n\n        <!-- 自动化配置响应式的 Spring Data R2DBC -->\n        <dependency>\n            <groupId>org.springframework.boot.experimental</groupId>\n            <artifactId>spring-boot-starter-data-r2dbc</artifactId>\n            <version>0.1.0.M2</version>\n        </dependency>\n\n        <!-- jasync 的 r2dbc-mysql 驱动 -->\n        <dependency>\n            <groupId>com.github.jasync-sql</groupId>\n            <artifactId>jasync-r2dbc-mysql</artifactId>\n            <version>1.0.11</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n    <repositories>\n        <!-- 引入 Spring 的快照仓库 -->\n        <repository>\n            <id>spring-libs-snapshot</id>\n            <url>https://repo.spring.io/libs-snapshot</url>\n        </repository>\n        <!-- 引入 Jcenter 的快照仓库 -->\n        <repository>\n            <id>jcenter</id>\n            <url>https://jcenter.bintray.com/</url>\n        </repository>\n    </repositories>\n\n</project>\n"
  },
  {
    "path": "lab-27/lab-27-webflux-r2dbc/src/main/java/cn/iocoder/springboot/lab27/springwebflux/Application.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-r2dbc/src/main/java/cn/iocoder/springboot/lab27/springwebflux/config/DatabaseConfiguration.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.config;\n\nimport com.github.jasync.r2dbc.mysql.JasyncConnectionFactory;\nimport com.github.jasync.sql.db.mysql.pool.MySQLConnectionFactory;\nimport io.r2dbc.spi.ConnectionFactory;\nimport org.springframework.boot.autoconfigure.r2dbc.R2dbcProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.r2dbc.connectionfactory.R2dbcTransactionManager;\nimport org.springframework.transaction.ReactiveTransactionManager;\nimport org.springframework.transaction.annotation.EnableTransactionManagement;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\n\n@Configuration\n@EnableTransactionManagement // 开启事务的支持\npublic class DatabaseConfiguration {\n\n    @Bean\n    public ConnectionFactory connectionFactory(R2dbcProperties properties) throws URISyntaxException {\n        // 从 R2dbcProperties 中，解析出 host、port、database\n        URI uri = new URI(properties.getUrl());\n        String host = uri.getHost();\n        int port = uri.getPort();\n        String database = uri.getPath().substring(1); // 去掉首位的 / 斜杠\n        // 创建 jasync Configuration 配置配置对象\n        com.github.jasync.sql.db.Configuration configuration = new com.github.jasync.sql.db.Configuration(\n                properties.getUsername(), host, port, properties.getPassword(), database);\n        // 创建 JasyncConnectionFactory 对象\n        return  new JasyncConnectionFactory(new MySQLConnectionFactory(configuration));\n    }\n\n    @Bean\n    public ReactiveTransactionManager transactionManager(R2dbcProperties properties) throws URISyntaxException {\n        return new R2dbcTransactionManager(this.connectionFactory(properties));\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-r2dbc/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.controller;\n\nimport cn.iocoder.springboot.lab27.springwebflux.dao.UserRepository;\nimport cn.iocoder.springboot.lab27.springwebflux.dataobject.UserDO;\nimport cn.iocoder.springboot.lab27.springwebflux.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab27.springwebflux.dto.UserUpdateDTO;\nimport cn.iocoder.springboot.lab27.springwebflux.vo.UserVO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.web.bind.annotation.*;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.util.Date;\nimport java.util.Objects;\nimport java.util.function.Function;\n\n/**\n * 用户 Controller\n */\n@RestController\n@RequestMapping(\"/users\")\npublic class UserController {\n\n    private static final UserDO USER_NULL = new UserDO();\n\n    @Autowired\n    private UserRepository userRepository;\n\n    /**\n     * 查询用户列表\n     *\n     * @return 用户列表\n     */\n    @GetMapping(\"/list\")\n    public Flux<UserVO> list() {\n        // 返回列表\n        return userRepository.findAll()\n                .map(userDO -> new UserVO().setId(userDO.getId()).setUsername(userDO.getUsername()));\n    }\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @GetMapping(\"/get\")\n    public Mono<UserVO> get(@RequestParam(\"id\") Integer id) {\n        // 返回\n        return userRepository.findById(id)\n                .map(userDO -> new UserVO().setId(userDO.getId()).setUsername(userDO.getUsername()));\n    }\n\n    /**\n     * 添加用户\n     *\n     * @param addDTO 添加用户信息 DTO\n     * @return 添加成功的用户编号\n     */\n    @PostMapping(\"add\")\n    @Transactional\n    public Mono<Integer> add(UserAddDTO addDTO) {\n        // 查询用户\n        Mono<UserDO> user = userRepository.findByUsername(addDTO.getUsername());\n\n        // 执行插入\n        return user.defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况，否则 flatMap 不会往下走\n                .flatMap(new Function<UserDO, Mono<Integer>>() {\n\n                    @Override\n                    public Mono<Integer> apply(UserDO userDO) {\n                        if (userDO != USER_NULL) {\n                            // 返回 -1 表示插入失败。\n                            // 实际上，一般是抛出 ServiceException 异常。因为这个示例项目里暂时没做全局异常的定义，所以暂时返回 -1 啦\n                            return Mono.just(-1);\n                        }\n                        // 将 addDTO 转成 UserDO\n                        userDO = new UserDO()\n                                .setUsername(addDTO.getUsername())\n                                .setPassword(addDTO.getPassword())\n                                .setCreateTime(new Date());\n                        // 插入数据库\n                        return userRepository.save(userDO).flatMap(new Function<UserDO, Mono<Integer>>() {\n                            @Override\n                            public Mono<Integer> apply(UserDO userDO) {\n                                // 如果编号为偶数，抛出异常。\n                                if (userDO.getId() % 2 == 0) {\n                                    throw new RuntimeException(\"我就是故意抛出一个异常，测试下事务回滚\");\n                                }\n\n                                // 返回编号\n                                return Mono.just(userDO.getId());\n                            }\n                        });\n                    }\n\n                });\n    }\n\n    /**\n     * 更新指定用户编号的用户\n     *\n     * @param updateDTO 更新用户信息 DTO\n     * @return 是否修改成功\n     */\n    @PostMapping(\"/update\")\n    public Mono<Boolean> update(UserUpdateDTO updateDTO) {\n        // 查询用户\n        Mono<UserDO> user = userRepository.findById(updateDTO.getId());\n\n        // 执行更新\n        return user.defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况，否则 flatMap 不会往下走\n                .flatMap(new Function<UserDO, Mono<Boolean>>() {\n\n                    @Override\n                    public Mono<Boolean> apply(UserDO userDO) {\n                        // 如果不存在该用户，则直接返回 false 失败\n                        if (userDO == USER_NULL) {\n                            return Mono.just(false);\n                        }\n                        // 查询用户是否存在\n                        return userRepository.findByUsername(updateDTO.getUsername())\n                                .defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况，否则 flatMap 不会往下走\n                                .flatMap(new Function<UserDO, Mono<? extends Boolean>>() {\n\n                                    @Override\n                                    public Mono<? extends Boolean> apply(UserDO usernameUserDO) {\n                                        // 如果用户名已经使用（该用户名对应的 id 不是自己，说明就已经被使用了）\n                                        if (usernameUserDO != USER_NULL && !Objects.equals(updateDTO.getId(), usernameUserDO.getId())) {\n                                            return Mono.just(false);\n                                        }\n                                        // 执行更新\n                                        userDO.setUsername(updateDTO.getUsername());\n                                        userDO.setPassword(updateDTO.getPassword());\n                                        return userRepository.save(userDO).map(userDO -> true); // 返回 true 成功\n                                    }\n\n                                });\n                    }\n\n                });\n    }\n\n    /**\n     * 删除指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 是否删除成功\n     */\n    @PostMapping(\"/delete\") // URL 修改成 /delete ，RequestMethod 改成 DELETE\n    public Mono<Boolean> delete(@RequestParam(\"id\") Integer id) {\n        // 查询用户\n        Mono<UserDO> user = userRepository.findById(id);\n\n        // 执行删除。这里仅仅是示例，项目中不要物理删除，而是标记删除\n        return user.defaultIfEmpty(USER_NULL) // 设置 USER_NULL 作为 null 的情况，否则 flatMap 不会往下走\n                .flatMap(new Function<UserDO, Mono<Boolean>>() {\n\n                    @Override\n                    public Mono<Boolean> apply(UserDO userDO) {\n                        // 如果不存在该用户，则直接返回 false 失败\n                        if (userDO == USER_NULL) {\n                            return Mono.just(false);\n                        }\n                        // 执行删除\n                        return userRepository.deleteById(id).map(aVoid -> true); // 返回 true 成功\n                    }\n\n                });\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-r2dbc/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dao/UserRepository.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.dao;\n\nimport cn.iocoder.springboot.lab27.springwebflux.dataobject.UserDO;\nimport org.springframework.data.r2dbc.repository.query.Query;\nimport org.springframework.data.repository.reactive.ReactiveCrudRepository;\nimport reactor.core.publisher.Mono;\n\npublic interface UserRepository extends ReactiveCrudRepository<UserDO, Integer> {\n\n    @Query(\"SELECT id, username, password, create_time FROM users WHERE username = :username\")\n    Mono<UserDO> findByUsername(String username);\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-r2dbc/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.dataobject;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.relational.core.mapping.Table;\n\nimport java.util.Date;\n\n/**\n * 用户 DO\n */\n@Table(value = \"users\")\npublic class UserDO {\n\n    @Id\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    public UserDO setCreateTime(Date createTime) {\n        this.createTime = createTime;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                \", createTime=\" + createTime +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-r2dbc/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.dto;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO {\n\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserAddDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserAddDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-r2dbc/src/main/java/cn/iocoder/springboot/lab27/springwebflux/dto/UserUpdateDTO.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.dto;\n\npublic class UserUpdateDTO {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserUpdateDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserUpdateDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserUpdateDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-r2dbc/src/main/java/cn/iocoder/springboot/lab27/springwebflux/vo/UserVO.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.vo;\n\n/**\n * 用户 VO\n */\npublic class UserVO {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserVO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserVO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-r2dbc/src/main/resources/application.yaml",
    "content": "spring:\n  # R2DBC 配置，对应 R2dbcProperties 配置类\n  r2dbc:\n    url: mysql://47.112.193.81:3306/lab-27-webflux-r2dbc\n    username: lab-27-webflux-r2dbc\n    password: 0ed86@11-r2Dbc123\n#  jasync:\n#    r2dbc:\n#      host: 47.112.193.81\n#      port: 3306\n#      database: lab-27-webflux-r2dbc\n#      username: lab-27-webflux-r2dbc\n#      password: 0ed86@11-r2Dbc123\n"
  },
  {
    "path": "lab-27/lab-27-webflux-r2dbc/src/main/resources/sql/users.sql",
    "content": "CREATE TABLE `users` (\n  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号',\n  `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',\n  `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码',\n  `create_time` datetime DEFAULT NULL COMMENT '创建时间',\n  PRIMARY KEY (`id`),\n  UNIQUE KEY `idx_username` (`username`)\n) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;\n"
  },
  {
    "path": "lab-27/lab-27-webflux-redis/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-27-webflux-redis</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring WebFlux 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n            <version>2.2.1.RELEASE</version>\n        </dependency>\n\n        <!-- 自动化配置响应式的 Spring Data Jedis -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-27/lab-27-webflux-redis/src/main/java/cn/iocoder/springboot/lab27/springwebflux/Application.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-redis/src/main/java/cn/iocoder/springboot/lab27/springwebflux/cacheobject/UserCacheObject.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.cacheobject;\n\n/**\n * 用户缓存对象\n */\npublic class UserCacheObject {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserCacheObject setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserCacheObject setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserCacheObject setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserCacheObject{\" +\n                \"id=\" + id +\n                \", name='\" + name + '\\'' +\n                \", gender=\" + gender +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-redis/src/main/java/cn/iocoder/springboot/lab27/springwebflux/config/RedisConfiguration.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.config;\n\nimport cn.iocoder.springboot.lab27.springwebflux.cacheobject.UserCacheObject;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;\nimport org.springframework.data.redis.core.ReactiveRedisTemplate;\nimport org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;\nimport org.springframework.data.redis.serializer.RedisSerializationContext;\nimport org.springframework.data.redis.serializer.RedisSerializer;\n\n@Configuration\npublic class RedisConfiguration {\n\n    @Bean\n    public ReactiveRedisTemplate<String, Object> commonRedisTemplate(ReactiveRedisConnectionFactory factory) {\n        RedisSerializationContext<String, Object> serializationContext =\n                RedisSerializationContext.<String, Object>newSerializationContext(RedisSerializer.string())\n                        .value(RedisSerializer.json()) // 创建通用的 GenericJackson2JsonRedisSerializer 作为序列化\n                        .build();\n        return new ReactiveRedisTemplate<>(factory, serializationContext);\n    }\n\n    @Bean\n    public ReactiveRedisTemplate<String, UserCacheObject> userRedisTemplate(ReactiveRedisConnectionFactory factory) {\n        RedisSerializationContext<String, UserCacheObject> serializationContext =\n                RedisSerializationContext.<String, UserCacheObject>newSerializationContext(RedisSerializer.string())\n                        .value(new Jackson2JsonRedisSerializer<>(UserCacheObject.class)) // 创建专属 UserCacheObject 的 Jackson2JsonRedisSerializer 作为序列化\n                        .build();\n        return new ReactiveRedisTemplate<>(factory, serializationContext);\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-redis/src/main/java/cn/iocoder/springboot/lab27/springwebflux/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab27.springwebflux.controller;\n\nimport cn.iocoder.springboot.lab27.springwebflux.cacheobject.UserCacheObject;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.redis.core.ReactiveRedisTemplate;\nimport org.springframework.web.bind.annotation.*;\nimport reactor.core.publisher.Mono;\n\n/**\n * 用户 Controller\n */\n@RestController\n@RequestMapping(\"/users\")\npublic class UserController {\n\n    // ========== 使用通用的 ReactiveRedisTemplate 的方式 ==========\n\n    @Autowired\n    private ReactiveRedisTemplate<String, Object> commonRedisTemplate;\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @GetMapping(\"/get\")\n    public Mono<UserCacheObject> get(@RequestParam(\"id\") Integer id) {\n        String key = genKey(id);\n        return commonRedisTemplate.opsForValue().get(key)\n                .map(o -> (UserCacheObject) o);\n    }\n\n    /**\n     * 设置指定用户的信息\n     *\n     * @param user 用户\n     * @return 是否成功\n     */\n    @PostMapping(\"/set\")\n    public Mono<Boolean> set(UserCacheObject user) {\n        String key = genKey(user.getId());\n        return commonRedisTemplate.opsForValue().set(key, user);\n    }\n\n    private static String genKey(Integer id) {\n        return \"user::\" + id;\n    }\n\n    // ========== 使用专属的 ReactiveRedisTemplate 的方式 =========\n\n    @Autowired\n    private ReactiveRedisTemplate<String, UserCacheObject> userRedisTemplate;\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @GetMapping(\"/v2/get\")\n    public Mono<UserCacheObject> getV2(@RequestParam(\"id\") Integer id) {\n        String key = genKeyV2(id);\n        return userRedisTemplate.opsForValue().get(key);\n    }\n\n    /**\n     * 设置指定用户的信息\n     *\n     * @param user 用户\n     * @return 是否成功\n     */\n    @PostMapping(\"/v2/set\")\n    public Mono<Boolean> setV2(UserCacheObject user) {\n        String key = genKeyV2(user.getId());\n        return userRedisTemplate.opsForValue().set(key, user);\n    }\n\n    private static String genKeyV2(Integer id) {\n        return \"user::v2::\" + id;\n    }\n\n}\n"
  },
  {
    "path": "lab-27/lab-27-webflux-redis/src/main/resources/application.yaml",
    "content": "spring:\n  # 对应 RedisProperties 类\n  redis:\n    host: 127.0.0.1\n    port: 6379\n    password: # Redis 服务器密码，默认为空。生产中，一定要设置 Redis 密码！\n    database: 0 # Redis 数据库号，默认为 0 。\n    timeout: 0 # Redis 连接超时时间，单位：毫秒。\n"
  },
  {
    "path": "lab-27/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-27</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-27-webflux-01</module>\n        <module>lab-27-webflux-02</module>\n        <module>lab-27-webflux-03</module>\n        <module>lab-27-webflux-mongodb</module>\n        <module>lab-27-webflux-redis</module>\n        <module>lab-27-webflux-elasticsearch</module>\n        <module>lab-27-webflux-r2dbc</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-27/《芋道 Spring Boot 响应式 WebFlux 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/WebFlux/?github>\n"
  },
  {
    "path": "lab-28/lab-28-task-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-28-task-demo</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-28/lab-28-task-demo/src/main/java/cn/iocoder/springboot/lab28/task/Application.java",
    "content": "package cn.iocoder.springboot.lab28.task;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-demo/src/main/java/cn/iocoder/springboot/lab28/task/config/ScheduleConfiguration.java",
    "content": "package cn.iocoder.springboot.lab28.task.config;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.scheduling.annotation.EnableScheduling;\n\n@Configuration\n@EnableScheduling\npublic class ScheduleConfiguration {\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-demo/src/main/java/cn/iocoder/springboot/lab28/task/job/DemoJob.java",
    "content": "package cn.iocoder.springboot.lab28.task.job;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.scheduling.annotation.Scheduled;\nimport org.springframework.stereotype.Component;\n\nimport java.util.concurrent.atomic.AtomicInteger;\n\n@Component\npublic class DemoJob {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    private final AtomicInteger counts = new AtomicInteger();\n\n    @Scheduled(fixedRate = 2000)\n    public void execute() {\n        logger.info(\"[execute][定时第 ({}) 次执行]\", counts.incrementAndGet());\n//        try {\n//            Thread.sleep(10000L);\n//        } catch (InterruptedException e) {\n//            e.printStackTrace();\n//        }\n    }\n\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-demo/src/main/resources/application.yaml",
    "content": "spring:\n  task:\n    # Spring Task 调度任务的配置，对应 TaskSchedulingProperties 配置类\n    scheduling:\n      thread-name-prefix: pikaqiu-demo- # 线程池的线程名的前缀。默认为 scheduling- ，建议根据自己应用来设置\n      pool:\n        size: 10 # 线程池大小。默认为 1 ，根据自己应用来设置\n      shutdown:\n        await-termination: true # 应用关闭时，是否等待定时任务执行完成。默认为 false ，建议设置为 true\n        await-termination-period: 60 # 等待任务完成的最大时长，单位为秒。默认为 0 ，根据自己应用来设置\n"
  },
  {
    "path": "lab-28/lab-28-task-quartz-jdbc/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.10.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-28-task-quartz-jdbc</artifactId>\n\n    <dependencies>\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Quartz 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-quartz</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-28/lab-28-task-quartz-jdbc/src/main/java/cn/iocoder/springboot/lab28/task/Application.java",
    "content": "package cn.iocoder.springboot.lab28.task;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-quartz-jdbc/src/main/java/cn/iocoder/springboot/lab28/task/Application02.java",
    "content": "package cn.iocoder.springboot.lab28.task;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application02 {\n\n    public static void main(String[] args) {\n        // 设置 Tomcat 随机端口\n        System.setProperty(\"server.port\", \"0\");\n\n        // 启动 Spring Boot 应用\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-quartz-jdbc/src/main/java/cn/iocoder/springboot/lab28/task/config/DataSourceConfiguration.java",
    "content": "package cn.iocoder.springboot.lab28.task.config;\n\nimport com.zaxxer.hikari.HikariDataSource;\nimport org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;\nimport org.springframework.boot.autoconfigure.quartz.QuartzDataSource;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.util.StringUtils;\n\nimport javax.sql.DataSource;\n\n@Configuration\npublic class DataSourceConfiguration {\n\n    /**\n     * 创建 user 数据源的配置对象\n     */\n    @Primary\n    @Bean(name = \"userDataSourceProperties\")\n    @ConfigurationProperties(prefix = \"spring.datasource.user\") // 读取 spring.datasource.user 配置到 DataSourceProperties 对象\n    public DataSourceProperties userDataSourceProperties() {\n        return new DataSourceProperties();\n    }\n\n    /**\n     * 创建 user 数据源\n     */\n    @Primary\n    @Bean(name = \"userDataSource\")\n    @ConfigurationProperties(prefix = \"spring.datasource.user.hikari\") // 读取 spring.datasource.user 配置到 HikariDataSource 对象\n    public DataSource userDataSource() {\n        // 获得 DataSourceProperties 对象\n        DataSourceProperties properties =  this.userDataSourceProperties();\n        // 创建 HikariDataSource 对象\n        return createHikariDataSource(properties);\n    }\n\n    /**\n     * 创建 quartz 数据源的配置对象\n     */\n    @Bean(name = \"quartzDataSourceProperties\")\n    @ConfigurationProperties(prefix = \"spring.datasource.quartz\") // 读取 spring.datasource.quartz 配置到 DataSourceProperties 对象\n    public DataSourceProperties quartzDataSourceProperties() {\n        return new DataSourceProperties();\n    }\n\n    /**\n     * 创建 quartz 数据源\n     */\n    @Bean(name = \"quartzDataSource\")\n    @ConfigurationProperties(prefix = \"spring.datasource.quartz.hikari\")\n    @QuartzDataSource\n    public DataSource quartzDataSource() {\n        // 获得 DataSourceProperties 对象\n        DataSourceProperties properties =  this.quartzDataSourceProperties();\n        // 创建 HikariDataSource 对象\n        return createHikariDataSource(properties);\n    }\n\n    private static HikariDataSource createHikariDataSource(DataSourceProperties properties) {\n        // 创建 HikariDataSource 对象\n        HikariDataSource dataSource = properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();\n        // 设置线程池名\n        if (StringUtils.hasText(properties.getName())) {\n            dataSource.setPoolName(properties.getName());\n        }\n        return dataSource;\n    }\n\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-quartz-jdbc/src/main/java/cn/iocoder/springboot/lab28/task/config/ScheduleConfiguration.java",
    "content": "package cn.iocoder.springboot.lab28.task.config;\n\nimport cn.iocoder.springboot.lab28.task.job.DemoJob01;\nimport cn.iocoder.springboot.lab28.task.job.DemoJob02;\nimport org.quartz.*;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class ScheduleConfiguration {\n\n    public static class DemoJob01Configuration {\n\n        @Bean\n        public JobDetail demoJob01() {\n            return JobBuilder.newJob(DemoJob01.class)\n                    .withIdentity(\"demoJob01\") // 名字为 demoJob01\n                    .storeDurably() // 没有 Trigger 关联的时候任务是否被保留。因为创建 JobDetail 时，还没 Trigger 指向它，所以需要设置为 true ，表示保留。\n                    .build();\n        }\n\n        @Bean\n        public Trigger demoJob01Trigger() {\n            // 简单的调度计划的构造器\n            SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()\n                    .withIntervalInSeconds(5) // 频率。\n                    .repeatForever(); // 次数。\n            // Trigger 构造器\n            return TriggerBuilder.newTrigger()\n                    .forJob(demoJob01()) // 对应 Job 为 demoJob01\n                    .withIdentity(\"demoJob01Trigger\") // 名字为 demoJob01Trigger\n                    .withSchedule(scheduleBuilder) // 对应 Schedule 为 scheduleBuilder\n                    .build();\n        }\n\n    }\n\n    public static class DemoJob02Configuration {\n\n        @Bean\n        public JobDetail demoJob02() {\n            return JobBuilder.newJob(DemoJob02.class)\n                    .withIdentity(\"demoJob02\") // 名字为 demoJob02\n                    .storeDurably() // 没有 Trigger 关联的时候任务是否被保留。因为创建 JobDetail 时，还没 Trigger 指向它，所以需要设置为 true ，表示保留。\n                    .build();\n        }\n\n        @Bean\n        public Trigger demoJob02Trigger() {\n            // 简单的调度计划的构造器\n            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(\"0/10 * * * * ? *\");\n            // Trigger 构造器\n            return TriggerBuilder.newTrigger()\n                    .forJob(demoJob02()) // 对应 Job 为 demoJob02\n                    .withIdentity(\"demoJob02Trigger\") // 名字为 demoJob02Trigger\n                    .withSchedule(scheduleBuilder) // 对应 Schedule 为 scheduleBuilder\n                    .build();\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-quartz-jdbc/src/main/java/cn/iocoder/springboot/lab28/task/job/DemoJob01.java",
    "content": "package cn.iocoder.springboot.lab28.task.job;\n\nimport cn.iocoder.springboot.lab28.task.service.DemoService;\nimport org.quartz.DisallowConcurrentExecution;\nimport org.quartz.JobExecutionContext;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.scheduling.quartz.QuartzJobBean;\n\n@DisallowConcurrentExecution\npublic class DemoJob01 extends QuartzJobBean {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private DemoService demoService;\n\n    @Override\n    protected void executeInternal(JobExecutionContext context) {\n        logger.info(\"[executeInternal][我开始的执行了, demoService 为 ({})]\", demoService);\n    }\n\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-quartz-jdbc/src/main/java/cn/iocoder/springboot/lab28/task/job/DemoJob02.java",
    "content": "package cn.iocoder.springboot.lab28.task.job;\n\nimport org.quartz.DisallowConcurrentExecution;\nimport org.quartz.JobExecutionContext;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.scheduling.quartz.QuartzJobBean;\n\n@DisallowConcurrentExecution\npublic class DemoJob02 extends QuartzJobBean {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    protected void executeInternal(JobExecutionContext context) {\n        logger.info(\"[executeInternal][我开始的执行了]\");\n    }\n\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-quartz-jdbc/src/main/java/cn/iocoder/springboot/lab28/task/service/DemoService.java",
    "content": "package cn.iocoder.springboot.lab28.task.service;\n\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class DemoService {\n\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-quartz-jdbc/src/main/resources/application.yaml",
    "content": "spring:\n  datasource:\n    user:\n      url: jdbc:mysql://127.0.0.1:3306/lab-28-quartz-jdbc-user?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n      driver-class-name: com.mysql.jdbc.Driver\n      username: root\n      password:\n    quartz:\n      url: jdbc:mysql://127.0.0.1:3306/lab-28-quartz-jdbc-quartz?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n      driver-class-name: com.mysql.jdbc.Driver\n      username: root\n      password:\n\n  # Quartz 的配置，对应 QuartzProperties 配置类\n  quartz:\n    scheduler-name: clusteredScheduler # Scheduler 名字。默认为 schedulerName\n    job-store-type: jdbc # Job 存储器类型。默认为 memory 表示内存，可选 jdbc 使用数据库。\n    auto-startup: true # Quartz 是否自动启动\n    startup-delay: 0 # 延迟 N 秒启动\n    wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时，是否等待定时任务执行完成。默认为 false ，建议设置为 true\n    overwrite-existing-jobs: false # 是否覆盖已有 Job 的配置\n    properties: # 添加 Quartz Scheduler 附加属性，更多可以看 http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/configuration.html 文档\n      org:\n        quartz:\n          # JobStore 相关配置\n          jobStore:\n            # 数据源名称\n            dataSource: quartzDataSource # 使用的数据源\n            class: org.quartz.impl.jdbcjobstore.JobStoreTX # JobStore 实现类\n            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate\n            tablePrefix: QRTZ_ # Quartz 表前缀\n            isClustered: true # 是集群模式\n            clusterCheckinInterval: 1000\n            useProperties: false\n          # 线程池相关配置\n          threadPool:\n            threadCount: 25 # 线程池大小。默认为 10 。\n            threadPriority: 5 # 线程优先级\n            class: org.quartz.simpl.SimpleThreadPool # 线程池类型\n    jdbc: # 使用 JDBC 的 JobStore 的时候，JDBC 的配置\n      initialize-schema: never # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ，我们手动创建表结构。\n"
  },
  {
    "path": "lab-28/lab-28-task-quartz-jdbc/src/test/java/cn/iocoder/springboot/lab28/task/QuartzSchedulerTest.java",
    "content": "package cn.iocoder.springboot.lab28.task;\n\nimport cn.iocoder.springboot.lab28.task.job.DemoJob01;\nimport cn.iocoder.springboot.lab28.task.job.DemoJob02;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.quartz.*;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class QuartzSchedulerTest {\n\n    @Autowired\n    private Scheduler scheduler;\n\n    @Test\n    public void addDemoJob01Config() throws SchedulerException {\n        // 创建 JobDetail\n        JobDetail jobDetail = JobBuilder.newJob(DemoJob01.class)\n                .withIdentity(\"demoJob01\") // 名字为 demoJob01\n                .storeDurably() // 没有 Trigger 关联的时候任务是否被保留。因为创建 JobDetail 时，还没 Trigger 指向它，所以需要设置为 true ，表示保留。\n                .build();\n        // 创建 Trigger\n        SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()\n                .withIntervalInSeconds(5) // 频率。\n                .repeatForever(); // 次数。\n        Trigger trigger = TriggerBuilder.newTrigger()\n                .forJob(jobDetail) // 对应 Job 为 demoJob01\n                .withIdentity(\"demoJob01Trigger\") // 名字为 demoJob01Trigger\n                .withSchedule(scheduleBuilder) // 对应 Schedule 为 scheduleBuilder\n                .build();\n        // 添加调度任务\n        scheduler.scheduleJob(jobDetail, trigger);\n    }\n\n    @Test\n    public void addDemoJob02Config() throws SchedulerException {\n        // 创建 JobDetail\n        JobDetail jobDetail = JobBuilder.newJob(DemoJob02.class)\n                .withIdentity(\"demoJob02\") // 名字为 demoJob02\n                .storeDurably() // 没有 Trigger 关联的时候任务是否被保留。因为创建 JobDetail 时，还没 Trigger 指向它，所以需要设置为 true ，表示保留。\n                .build();\n        // 创建 Trigger\n        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(\"0/10 * * * * ? *\");\n        Trigger trigger = TriggerBuilder.newTrigger()\n                .forJob(jobDetail) // 对应 Job 为 demoJob01\n                .withIdentity(\"demoJob02Trigger\") // 名字为 demoJob01Trigger\n                .withSchedule(scheduleBuilder) // 对应 Schedule 为 scheduleBuilder\n                .build();\n        // 添加调度任务\n        scheduler.scheduleJob(jobDetail, trigger);\n//        scheduler.scheduleJob(jobDetail, Sets.newSet(trigger), true);\n    }\n\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-quartz-memory/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-28-task-quartz-memory</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Quartz 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-quartz</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-28/lab-28-task-quartz-memory/src/main/java/cn/iocoder/springboot/lab28/task/Application.java",
    "content": "package cn.iocoder.springboot.lab28.task;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-quartz-memory/src/main/java/cn/iocoder/springboot/lab28/task/config/ScheduleConfiguration.java",
    "content": "package cn.iocoder.springboot.lab28.task.config;\n\nimport cn.iocoder.springboot.lab28.task.job.DemoJob01;\nimport cn.iocoder.springboot.lab28.task.job.DemoJob02;\nimport org.quartz.*;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class ScheduleConfiguration {\n\n    public static class DemoJob01Configuration {\n\n        @Bean\n        public JobDetail demoJob01() {\n            return JobBuilder.newJob(DemoJob01.class)\n                    .withIdentity(\"demoJob01\") // 名字为 demoJob01\n                    .storeDurably() // 没有 Trigger 关联的时候任务是否被保留。因为创建 JobDetail 时，还没 Trigger 指向它，所以需要设置为 true ，表示保留。\n                    .build();\n        }\n\n        @Bean\n        public Trigger demoJob01Trigger() {\n            // 简单的调度计划的构造器\n            SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()\n                    .withIntervalInSeconds(5) // 频率。\n                    .repeatForever(); // 次数。\n            // Trigger 构造器\n            return TriggerBuilder.newTrigger()\n                    .forJob(demoJob01()) // 对应 Job 为 demoJob01\n                    .withIdentity(\"demoJob01Trigger\") // 名字为 demoJob01Trigger\n                    .withSchedule(scheduleBuilder) // 对应 Schedule 为 scheduleBuilder\n                    .build();\n        }\n\n    }\n\n    public static class DemoJob02Configuration {\n\n        @Bean\n        public JobDetail demoJob02() {\n            return JobBuilder.newJob(DemoJob02.class)\n                    .withIdentity(\"demoJob02\") // 名字为 demoJob02\n                    .storeDurably() // 没有 Trigger 关联的时候任务是否被保留。因为创建 JobDetail 时，还没 Trigger 指向它，所以需要设置为 true ，表示保留。\n                    .build();\n        }\n\n        @Bean\n        public Trigger demoJob02Trigger() {\n            //  基于 Quartz Cron 表达式的调度计划的构造器\n            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(\"0/10 * * * * ? *\");\n            // Trigger 构造器\n            return TriggerBuilder.newTrigger()\n                    .forJob(demoJob02()) // 对应 Job 为 demoJob02\n                    .withIdentity(\"demoJob02Trigger\") // 名字为 demoJob02Trigger\n                    .withSchedule(scheduleBuilder) // 对应 Schedule 为 scheduleBuilder\n                    .build();\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-quartz-memory/src/main/java/cn/iocoder/springboot/lab28/task/job/DemoJob01.java",
    "content": "package cn.iocoder.springboot.lab28.task.job;\n\nimport cn.iocoder.springboot.lab28.task.service.DemoService;\nimport org.quartz.JobExecutionContext;\nimport org.quartz.JobExecutionException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.scheduling.quartz.QuartzJobBean;\n\nimport java.util.concurrent.atomic.AtomicInteger;\n\npublic class DemoJob01 extends QuartzJobBean {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    private final AtomicInteger counts = new AtomicInteger();\n\n    @Autowired\n    private DemoService demoService;\n\n    @Override\n    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {\n        logger.info(\"[executeInternal][定时第 ({}) 次执行, demoService 为 ({})]\", counts.incrementAndGet(),\n                demoService);\n    }\n\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-quartz-memory/src/main/java/cn/iocoder/springboot/lab28/task/job/DemoJob02.java",
    "content": "package cn.iocoder.springboot.lab28.task.job;\n\nimport org.quartz.JobExecutionContext;\nimport org.quartz.JobExecutionException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.scheduling.quartz.QuartzJobBean;\n\npublic class DemoJob02 extends QuartzJobBean {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {\n        logger.info(\"[executeInternal][我开始的执行了]\");\n    }\n\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-quartz-memory/src/main/java/cn/iocoder/springboot/lab28/task/service/DemoService.java",
    "content": "package cn.iocoder.springboot.lab28.task.service;\n\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class DemoService {\n\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-quartz-memory/src/main/resources/application.yaml",
    "content": "spring:\n  # Quartz 的配置，对应 QuartzProperties 配置类\n  quartz:\n    job-store-type: memory # Job 存储器类型。默认为 memory 表示内存，可选 jdbc 使用数据库。\n    auto-startup: true # Quartz 是否自动启动\n    startup-delay: 0 # 延迟 N 秒启动\n    wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时，是否等待定时任务执行完成。默认为 false ，建议设置为 true\n    overwrite-existing-jobs: false # 是否覆盖已有 Job 的配置\n    properties: # 添加 Quartz Scheduler 附加属性，更多可以看 http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/configuration.html 文档\n      org:\n        quartz:\n          threadPool:\n            threadCount: 25 # 线程池大小。默认为 10 。\n            threadPriority: 5 # 线程优先级\n            class: org.quartz.simpl.SimpleThreadPool # 线程池类型\n#    jdbc: # 这里暂时不说明，使用 JDBC 的 JobStore 的时候，才需要配置\n"
  },
  {
    "path": "lab-28/lab-28-task-xxl-job/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-28-task-xxl-job</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- XXL-JOB 相关依赖 -->\n        <dependency>\n            <groupId>com.xuxueli</groupId>\n            <artifactId>xxl-job-core</artifactId>\n            <version>2.1.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-28/lab-28-task-xxl-job/src/main/java/cn/iocoder/springboot/lab28/task/Application.java",
    "content": "package cn.iocoder.springboot.lab28.task;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-xxl-job/src/main/java/cn/iocoder/springboot/lab28/task/config/XxlJobConfiguration.java",
    "content": "package cn.iocoder.springboot.lab28.task.config;\n\nimport com.xxl.job.core.executor.impl.XxlJobSpringExecutor;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class XxlJobConfiguration {\n\n    @Value(\"${xxl.job.admin.addresses}\")\n    private String adminAddresses;\n    @Value(\"${xxl.job.executor.appname}\")\n    private String appName;\n    @Value(\"${xxl.job.executor.ip}\")\n    private String ip;\n    @Value(\"${xxl.job.executor.port}\")\n    private int port;\n    @Value(\"${xxl.job.accessToken}\")\n    private String accessToken;\n    @Value(\"${xxl.job.executor.logpath}\")\n    private String logPath;\n    @Value(\"${xxl.job.executor.logretentiondays}\")\n    private int logRetentionDays;\n\n    @Bean\n    public XxlJobSpringExecutor xxlJobExecutor() {\n        // 创建 XxlJobSpringExecutor 执行器\n        XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();\n        xxlJobSpringExecutor.setAdminAddresses(adminAddresses);\n        xxlJobSpringExecutor.setAppName(appName);\n        xxlJobSpringExecutor.setIp(ip);\n        xxlJobSpringExecutor.setPort(port);\n        xxlJobSpringExecutor.setAccessToken(accessToken);\n        xxlJobSpringExecutor.setLogPath(logPath);\n        xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);\n        // 返回\n        return xxlJobSpringExecutor;\n    }\n\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-xxl-job/src/main/java/cn/iocoder/springboot/lab28/task/job/DemoJob.java",
    "content": "package cn.iocoder.springboot.lab28.task.job;\n\nimport com.xxl.job.core.biz.model.ReturnT;\nimport com.xxl.job.core.handler.IJobHandler;\nimport com.xxl.job.core.handler.annotation.JobHandler;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\nimport java.util.concurrent.atomic.AtomicInteger;\n\n@Component\n@JobHandler(\"demoJob\")\npublic class DemoJob extends IJobHandler {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    private final AtomicInteger counts = new AtomicInteger();\n\n    @Override\n    public ReturnT<String> execute(String param) throws Exception {\n        // 打印日志\n        logger.info(\"[execute][定时第 ({}) 次执行]\", counts.incrementAndGet());\n        // 返回执行成功\n        return ReturnT.SUCCESS;\n    }\n\n}\n"
  },
  {
    "path": "lab-28/lab-28-task-xxl-job/src/main/resources/application.yaml",
    "content": "server:\n  port: 9090 #指定一个端口，避免和 XXL-JOB 调度中心的端口冲突。仅仅测试之用\n\n# xxl-job\nxxl:\n  job:\n    admin:\n      addresses: http://127.0.0.1:8080/xxl-job-admin # 调度中心部署跟地址 [选填]：如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行\"执行器心跳注册\"和\"任务结果回调\"；为空则关闭自动注册；\n    executor:\n      appname: lab-28-executor # 执行器 AppName [选填]：执行器心跳注册分组依据；为空则关闭自动注册\n      ip: # 执行器IP [选填]：默认为空表示自动获取IP，多网卡时可手动设置指定IP，该IP不会绑定Host仅作为通讯实用；地址信息用于 \"执行器注册\" 和 \"调度中心请求并触发任务\"；\n      port: 6666 # ### 执行器端口号 [选填]：小于等于0则自动获取；默认端口为9999，单机部署多个执行器时，注意要配置不同执行器端口；\n      logpath: /Users/yunai/logs/xxl-job/lab-28-executor # 执行器运行日志文件存储磁盘路径 [选填] ：需要对该路径拥有读写权限；为空则使用默认路径；\n      logretentiondays: 30 # 执行器日志文件保存天数 [选填] ： 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能；\n    accessToken: yudaoyuanma # 执行器通讯TOKEN [选填]：非空时启用；\n"
  },
  {
    "path": "lab-28/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-28</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-28-task-demo</module>\n        <module>lab-28-task-quartz-memory</module>\n        <module>lab-28-task-quartz-jdbc</module>\n        <module>lab-28-task-xxl-job</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-28/《芋道 Spring Boot 定时任务入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Job/?github>\n"
  },
  {
    "path": "lab-29/lab-29-async-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-29-async-demo</artifactId>\n\n    <dependencies>\n        <!-- 引入 Spring Boot 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-29/lab-29-async-demo/src/main/java/cn/iocoder/springboot/lab29/asynctask/Application.java",
    "content": "package cn.iocoder.springboot.lab29.asynctask;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.scheduling.annotation.EnableAsync;\n\n@SpringBootApplication\n@EnableAsync // 开启 @Async 的支持\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-29/lab-29-async-demo/src/main/java/cn/iocoder/springboot/lab29/asynctask/Demo.java",
    "content": "package cn.iocoder.springboot.lab29.asynctask;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\npublic class Demo {\n\n    public static void main(String[] args) {\n        // 创建线程池。这里只是临时测试，不要扣艿艿遵守阿里 Java 开发规范，YEAH\n        ExecutorService executor = Executors.newFixedThreadPool(10);\n\n        // 提交任务到线程池中执行。\n        executor.submit(new Runnable() {\n\n            @Override\n            public void run() {\n                System.out.println(\"听说我被异步调用了\");\n            }\n\n        });\n    }\n\n}\n"
  },
  {
    "path": "lab-29/lab-29-async-demo/src/main/java/cn/iocoder/springboot/lab29/asynctask/config/AsyncConfig.java",
    "content": "package cn.iocoder.springboot.lab29.asynctask.config;\n\nimport cn.iocoder.springboot.lab29.asynctask.core.async.GlobalAsyncExceptionHandler;\nimport org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.scheduling.annotation.AsyncConfigurer;\nimport org.springframework.scheduling.annotation.EnableAsync;\n\nimport java.util.concurrent.Executor;\n\n@Configuration\n@EnableAsync // 开启 @Async 的支持\npublic class AsyncConfig implements AsyncConfigurer {\n\n    @Autowired\n    private GlobalAsyncExceptionHandler exceptionHandler;\n\n    @Override\n    public Executor getAsyncExecutor() {\n        return null;\n    }\n\n    @Override\n    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {\n        return exceptionHandler;\n    }\n\n}\n"
  },
  {
    "path": "lab-29/lab-29-async-demo/src/main/java/cn/iocoder/springboot/lab29/asynctask/core/async/GlobalAsyncExceptionHandler.java",
    "content": "package cn.iocoder.springboot.lab29.asynctask.core.async;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;\nimport org.springframework.stereotype.Component;\n\nimport java.lang.reflect.Method;\n\n@Component\npublic class GlobalAsyncExceptionHandler implements AsyncUncaughtExceptionHandler {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void handleUncaughtException(Throwable ex, Method method, Object... params) {\n        logger.error(\"[handleUncaughtException][method({}) params({}) 发生异常]\",\n                method, params, ex);\n    }\n\n}\n"
  },
  {
    "path": "lab-29/lab-29-async-demo/src/main/java/cn/iocoder/springboot/lab29/asynctask/core/package-info.java",
    "content": "/**\n * 核心封装\n */\npackage cn.iocoder.springboot.lab29.asynctask.core;\n"
  },
  {
    "path": "lab-29/lab-29-async-demo/src/main/java/cn/iocoder/springboot/lab29/asynctask/service/DemoService.java",
    "content": "package cn.iocoder.springboot.lab29.asynctask.service;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.scheduling.annotation.AsyncResult;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.concurrent.ListenableFuture;\n\nimport java.util.concurrent.Future;\n\n@Service\npublic class DemoService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n//    public void task01() {\n//        long now = System.currentTimeMillis();\n//        logger.info(\"[task01][开始执行]\");\n//\n//        execute01();\n//        execute02();\n//\n//        logger.info(\"[task01][结束执行，消耗时长 {} 毫秒]\", System.currentTimeMillis() - now);\n//    }\n//\n//    public void task02() {\n//        long now = System.currentTimeMillis();\n//        logger.info(\"[task02][开始执行]\");\n//\n//        execute01Async();\n//        execute02Async();\n//\n//        logger.info(\"[task02][结束执行，消耗时长 {} 毫秒]\", System.currentTimeMillis() - now);\n//    }\n//\n//    public void task03() throws ExecutionException, InterruptedException {\n//        long now = System.currentTimeMillis();\n//        logger.info(\"[task03][开始执行]\");\n//\n//        // 执行任务\n//        Future<Integer> execute01Result = execute01AsyncWithFuture();\n//        Future<Integer> execute02Result = execute02AsyncWithFuture();\n//        // 阻塞等待结果\n//        execute01Result.get();\n//        execute02Result.get();\n//\n//        logger.info(\"[task03][结束执行，消耗时长 {} 毫秒]\", System.currentTimeMillis() - now);\n//    }\n\n    @Async\n    public Integer execute01Async() {\n        return this.execute01();\n    }\n\n    @Async\n    public Integer execute02Async() {\n        return this.execute02();\n    }\n\n    @Async\n    public Future<Integer> execute01AsyncWithFuture() {\n        return AsyncResult.forValue(this.execute01());\n    }\n\n    @Async\n    public Future<Integer> execute02AsyncWithFuture() {\n        return AsyncResult.forValue(this.execute02());\n    }\n\n    @Async\n    public ListenableFuture<Integer> execute01AsyncWithListenableFuture() {\n        try {\n            return AsyncResult.forValue(this.execute02());\n        } catch (Throwable ex) {\n            return AsyncResult.forExecutionException(ex);\n        }\n    }\n\n    public Integer execute01() {\n        logger.info(\"[execute01]\");\n        sleep(10);\n        return 1;\n    }\n\n    public Integer execute02() {\n        logger.info(\"[execute02]\");\n        sleep(5);\n        return 2;\n    }\n\n    private static void sleep(int seconds) {\n        try {\n            Thread.sleep(seconds * 1000);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Async\n    public Integer zhaoDaoNvPengYou(Integer a, Integer b) {\n        throw new RuntimeException(\"程序员不需要女朋友\");\n    }\n\n}\n"
  },
  {
    "path": "lab-29/lab-29-async-demo/src/main/resources/application.yaml",
    "content": "spring:\n  task:\n    # Spring 执行器配置，对应 TaskExecutionProperties 配置类。对于 Spring 异步任务，会使用该执行器。\n    execution:\n      thread-name-prefix: task- # 线程池的线程名的前缀。默认为 task- ，建议根据自己应用来设置\n      pool: # 线程池相关\n        core-size: 8 # 核心线程数，线程池创建时候初始化的线程数。默认为 8 。\n        max-size: 20 # 最大线程数，线程池最大的线程数，只有在缓冲队列满了之后，才会申请超过核心线程数的线程。默认为 Integer.MAX_VALUE\n        keep-alive: 60s # 允许线程的空闲时间，当超过了核心线程之外的线程，在空闲时间到达之后会被销毁。默认为 60 秒\n        queue-capacity: 200 # 缓冲队列大小，用来缓冲执行任务的队列的大小。默认为 Integer.MAX_VALUE 。\n        allow-core-thread-timeout: true # 是否允许核心线程超时，即开启线程池的动态增长和缩小。默认为 true 。\n      shutdown:\n        await-termination: true # 应用关闭时，是否等待定时任务执行完成。默认为 false ，建议设置为 true\n        await-termination-period: 60 # 等待任务完成的最大时长，单位为秒。默认为 0 ，根据自己应用来设置\n"
  },
  {
    "path": "lab-29/lab-29-async-demo/src/test/java/cn/iocoder/springboot/lab29/asynctask/package-info.java",
    "content": "package cn.iocoder.springboot.lab29.asynctask;\n"
  },
  {
    "path": "lab-29/lab-29-async-demo/src/test/java/cn/iocoder/springboot/lab29/asynctask/service/DemoServiceTest.java",
    "content": "package cn.iocoder.springboot.lab29.asynctask.service;\n\nimport cn.iocoder.springboot.lab29.asynctask.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.util.concurrent.FailureCallback;\nimport org.springframework.util.concurrent.ListenableFuture;\nimport org.springframework.util.concurrent.ListenableFutureCallback;\nimport org.springframework.util.concurrent.SuccessCallback;\n\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class DemoServiceTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private DemoService demoService;\n\n    @Test\n    public void task01() {\n        long now = System.currentTimeMillis();\n        logger.info(\"[task01][开始执行]\");\n\n        demoService.execute01();\n        demoService.execute02();\n\n        logger.info(\"[task01][结束执行，消耗时长 {} 毫秒]\", System.currentTimeMillis() - now);\n    }\n\n    @Test\n    public void task02() {\n        long now = System.currentTimeMillis();\n        logger.info(\"[task02][开始执行]\");\n\n        demoService.execute01Async();\n        demoService.execute02Async();\n\n        logger.info(\"[task02][结束执行，消耗时长 {} 毫秒]\", System.currentTimeMillis() - now);\n    }\n\n    @Test\n    public void task03() throws ExecutionException, InterruptedException {\n        long now = System.currentTimeMillis();\n        logger.info(\"[task03][开始执行]\");\n\n        // 执行任务\n        Future<Integer> execute01Result = demoService.execute01AsyncWithFuture();\n        Future<Integer> execute02Result = demoService.execute02AsyncWithFuture();\n        // 阻塞等待结果\n        execute01Result.get();\n        execute02Result.get();\n\n        logger.info(\"[task03][结束执行，消耗时长 {} 毫秒]\", System.currentTimeMillis() - now);\n    }\n\n    @Test\n    public void task04() throws ExecutionException, InterruptedException {\n        long now = System.currentTimeMillis();\n        logger.info(\"[task04][开始执行]\");\n\n        // 执行任务\n        ListenableFuture<Integer> execute01Result = demoService.execute01AsyncWithListenableFuture();\n        logger.info(\"[task04][execute01Result 的类型是：({})]\",execute01Result.getClass().getSimpleName());\n        execute01Result.addCallback(new SuccessCallback<Integer>() { // 增加成功的回调\n\n            @Override\n            public void onSuccess(Integer result) {\n                logger.info(\"[onSuccess][result: {}]\", result);\n            }\n\n        }, new FailureCallback() { // 增加失败的回调\n\n            @Override\n            public void onFailure(Throwable ex) {\n                logger.info(\"[onFailure][发生异常]\", ex);\n            }\n\n        });\n        execute01Result.addCallback(new ListenableFutureCallback<Integer>() { // 增加成功和失败的统一回调\n\n            @Override\n            public void onSuccess(Integer result) {\n                logger.info(\"[onSuccess][result: {}]\", result);\n            }\n\n            @Override\n            public void onFailure(Throwable ex) {\n                logger.info(\"[onFailure][发生异常]\", ex);\n            }\n\n        });\n        // 阻塞等待结果\n        execute01Result.get();\n\n        logger.info(\"[task04][结束执行，消耗时长 {} 毫秒]\", System.currentTimeMillis() - now);\n    }\n\n    @Test\n    public void testZhaoDaoNvPengYou() throws InterruptedException {\n        demoService.zhaoDaoNvPengYou(1, 2);\n\n        // sleep 1 秒，保证异步调用的执行\n        Thread.sleep(1000);\n    }\n\n}\n"
  },
  {
    "path": "lab-29/lab-29-async-two/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-29-async-two</artifactId>\n\n    <dependencies>\n        <!-- 引入 Spring Boot 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-29/lab-29-async-two/src/main/java/cn/iocoder/springboot/lab29/asynctask/Application.java",
    "content": "package cn.iocoder.springboot.lab29.asynctask;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-29/lab-29-async-two/src/main/java/cn/iocoder/springboot/lab29/asynctask/config/AsyncConfig.java",
    "content": "package cn.iocoder.springboot.lab29.asynctask.config;\n\nimport org.springframework.boot.autoconfigure.task.TaskExecutionProperties;\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.boot.task.TaskExecutorBuilder;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.scheduling.annotation.EnableAsync;\nimport org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;\n\n@Configuration\n@EnableAsync // 开启 @Async 的支持\npublic class AsyncConfig {\n\n    public static final String EXECUTOR_ONE_BEAN_NAME = \"executor-one\";\n    public static final String EXECUTOR_TWO_BEAN_NAME = \"executor-two\";\n\n    @Configuration\n    public static class ExecutorOneConfiguration {\n\n        @Bean(name = EXECUTOR_ONE_BEAN_NAME + \"-properties\")\n        @Primary\n        @ConfigurationProperties(prefix = \"spring.task.execution-one\") // 读取 spring.task.execution-one 配置到 TaskExecutionProperties 对象\n        public TaskExecutionProperties taskExecutionProperties() {\n            return new TaskExecutionProperties();\n        }\n\n        @Bean(name = EXECUTOR_ONE_BEAN_NAME)\n        public ThreadPoolTaskExecutor threadPoolTaskExecutor() {\n            // 创建 TaskExecutorBuilder 对象\n            TaskExecutorBuilder builder = createTskExecutorBuilder(this.taskExecutionProperties());\n            // 创建 ThreadPoolTaskExecutor 对象\n            return builder.build();\n        }\n\n    }\n\n    @Configuration\n    public static class ExecutorTwoConfiguration {\n\n        @Bean(name = EXECUTOR_TWO_BEAN_NAME + \"-properties\")\n        @ConfigurationProperties(prefix = \"spring.task.execution-two\") // 读取 spring.task.execution-two 配置到 TaskExecutionProperties 对象\n        public TaskExecutionProperties taskExecutionProperties() {\n            return new TaskExecutionProperties();\n        }\n\n        @Bean(name = EXECUTOR_TWO_BEAN_NAME)\n        public ThreadPoolTaskExecutor threadPoolTaskExecutor() {\n            // 创建 TaskExecutorBuilder 对象\n            TaskExecutorBuilder builder = createTskExecutorBuilder(this.taskExecutionProperties());\n            // 创建 ThreadPoolTaskExecutor 对象\n            return builder.build();\n        }\n\n    }\n\n    private static TaskExecutorBuilder createTskExecutorBuilder(TaskExecutionProperties properties) {\n        // Pool 属性\n        TaskExecutionProperties.Pool pool = properties.getPool();\n        TaskExecutorBuilder builder = new TaskExecutorBuilder();\n        builder = builder.queueCapacity(pool.getQueueCapacity());\n        builder = builder.corePoolSize(pool.getCoreSize());\n        builder = builder.maxPoolSize(pool.getMaxSize());\n        builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout());\n        builder = builder.keepAlive(pool.getKeepAlive());\n        // Shutdown 属性\n        TaskExecutionProperties.Shutdown shutdown = properties.getShutdown();\n        builder = builder.awaitTermination(shutdown.isAwaitTermination());\n        builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod());\n        // 其它基本属性\n        builder = builder.threadNamePrefix(properties.getThreadNamePrefix());\n//        builder = builder.customizers(taskExecutorCustomizers.orderedStream()::iterator);\n//        builder = builder.taskDecorator(taskDecorator.getIfUnique());\n        return builder;\n    }\n\n}\n"
  },
  {
    "path": "lab-29/lab-29-async-two/src/main/java/cn/iocoder/springboot/lab29/asynctask/service/DemoService.java",
    "content": "package cn.iocoder.springboot.lab29.asynctask.service;\n\nimport cn.iocoder.springboot.lab29.asynctask.config.AsyncConfig;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class DemoService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Async(AsyncConfig.EXECUTOR_ONE_BEAN_NAME)\n    public Integer execute01() {\n        logger.info(\"[execute01]\");\n        return 1;\n    }\n\n    @Async(AsyncConfig.EXECUTOR_TWO_BEAN_NAME)\n    public Integer execute02() {\n        logger.info(\"[execute02]\");\n        return 2;\n    }\n\n}\n"
  },
  {
    "path": "lab-29/lab-29-async-two/src/main/resources/application.yaml",
    "content": "spring:\n  task:\n    # Spring 执行器配置，对应 TaskExecutionProperties 配置类。对于 Spring 异步任务，会使用该执行器。\n    execution-one:\n      thread-name-prefix: task-one- # 线程池的线程名的前缀。默认为 task- ，建议根据自己应用来设置\n      pool: # 线程池相关\n        core-size: 8 # 核心线程数，线程池创建时候初始化的线程数。默认为 8 。\n        max-size: 20 # 最大线程数，线程池最大的线程数，只有在缓冲队列满了之后，才会申请超过核心线程数的线程。默认为 Integer.MAX_VALUE\n        keep-alive: 60s # 允许线程的空闲时间，当超过了核心线程之外的线程，在空闲时间到达之后会被销毁。默认为 60 秒\n        queue-capacity: 200 # 缓冲队列大小，用来缓冲执行任务的队列的大小。默认为 Integer.MAX_VALUE 。\n        allow-core-thread-timeout: true # 是否允许核心线程超时，即开启线程池的动态增长和缩小。默认为 true 。\n      shutdown:\n        await-termination: true # 应用关闭时，是否等待定时任务执行完成。默认为 false ，建议设置为 true\n        await-termination-period: 60 # 等待任务完成的最大时长，单位为秒。默认为 0 ，根据自己应用来设置\n    # Spring 执行器配置，对应 TaskExecutionProperties 配置类。对于 Spring 异步任务，会使用该执行器。\n    execution-two:\n      thread-name-prefix: task-two- # 线程池的线程名的前缀。默认为 task- ，建议根据自己应用来设置\n      pool: # 线程池相关\n        core-size: 8 # 核心线程数，线程池创建时候初始化的线程数。默认为 8 。\n        max-size: 20 # 最大线程数，线程池最大的线程数，只有在缓冲队列满了之后，才会申请超过核心线程数的线程。默认为 Integer.MAX_VALUE\n        keep-alive: 60s # 允许线程的空闲时间，当超过了核心线程之外的线程，在空闲时间到达之后会被销毁。默认为 60 秒\n        queue-capacity: 200 # 缓冲队列大小，用来缓冲执行任务的队列的大小。默认为 Integer.MAX_VALUE 。\n        allow-core-thread-timeout: true # 是否允许核心线程超时，即开启线程池的动态增长和缩小。默认为 true 。\n      shutdown:\n        await-termination: true # 应用关闭时，是否等待定时任务执行完成。默认为 false ，建议设置为 true\n        await-termination-period: 60 # 等待任务完成的最大时长，单位为秒。默认为 0 ，根据自己应用来设置\n"
  },
  {
    "path": "lab-29/lab-29-async-two/src/test/java/cn/iocoder/springboot/lab29/asynctask/package-info.java",
    "content": "package cn.iocoder.springboot.lab29.asynctask;\n"
  },
  {
    "path": "lab-29/lab-29-async-two/src/test/java/cn/iocoder/springboot/lab29/asynctask/service/DemoServiceTest.java",
    "content": "package cn.iocoder.springboot.lab29.asynctask.service;\n\nimport cn.iocoder.springboot.lab29.asynctask.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class DemoServiceTest {\n\n    @Autowired\n    private DemoService demoService;\n\n    @Test\n    public void testExecute() throws InterruptedException {\n        demoService.execute01();\n        demoService.execute02();\n\n        // sleep 1 秒，保证异步调用的执行\n        Thread.sleep(1000);\n    }\n\n}\n"
  },
  {
    "path": "lab-29/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-29</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-29-async-demo</module>\n        <module>lab-29-async-two</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-29/《芋道 Spring Boot 异步任务入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Async-Job/?github>\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-30</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-30-dubbo-annotations-demo</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>user-rpc-service-api-02</module>\n        <module>user-rpc-service-provider-02</module>\n        <module>user-rpc-service-consumer-02</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-api-02/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-30-dubbo-annotations-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>user-rpc-service-api-02</artifactId>\n\n\n</project>\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-api-02/src/main/java/cn/iocoder/springboot/lab30/rpc/api/UserRpcService.java",
    "content": "package cn.iocoder.springboot.lab30.rpc.api;\n\nimport cn.iocoder.springboot.lab30.rpc.dto.UserDTO;\n\n/**\n * 用户服务 RPC Service 接口\n */\npublic interface UserRpcService {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    UserDTO get(Integer id);\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-api-02/src/main/java/cn/iocoder/springboot/lab30/rpc/dto/UserDTO.java",
    "content": "package cn.iocoder.springboot.lab30.rpc.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户信息 DTO\n */\npublic class UserDTO implements Serializable {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-consumer-02/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>user-rpc-service-consumer-02</artifactId>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>user-rpc-service-api-02</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Boot 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对 Dubbo 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n        <!-- 使用 Zookeeper 作为注册中心 -->\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-framework</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-recipes</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-consumer-02/src/main/java/cn/iocoder/springboot/lab30/rpc/ConsumerApplication.java",
    "content": "package cn.iocoder.springboot.lab30.rpc;\n\nimport cn.iocoder.springboot.lab30.rpc.api.UserRpcService;\nimport cn.iocoder.springboot.lab30.rpc.dto.UserDTO;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.stereotype.Component;\n\n@SpringBootApplication\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        ConfigurableApplicationContext context = SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n    @Component\n    public class UserRpcServiceTest implements CommandLineRunner {\n\n        private final Logger logger = LoggerFactory.getLogger(getClass());\n\n        @Reference(version = \"${dubbo.consumer.UserRpcService.version}\")\n        private UserRpcService userRpcService;\n\n        @Override\n        public void run(String... args) throws Exception {\n            UserDTO user = userRpcService.get(1);\n            logger.info(\"[run][发起一次 Dubbo RPC 请求，获得用户为({})\", user);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-consumer-02/src/main/resources/application.yaml",
    "content": "# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: user-service-consumer # 应用名\n  # Dubbo 注册中心配置\n  registry:\n    address: zookeeper://127.0.0.1:2181 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 消费者配置\n  consumer:\n    timeout: 1000 # 【重要】远程服务调用超时时间，单位：毫秒。默认为 1000 毫秒，胖友可以根据自己业务修改\n    UserRpcService:\n      version: 1.0.0\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-provider-02/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>user-rpc-service-provider-02</artifactId>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>user-rpc-service-api-02</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Boot 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对 Dubbo 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n        <!-- 使用 Zookeeper 作为注册中心 -->\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-framework</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-recipes</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-provider-02/src/main/java/cn/iocoder/springboot/lab30/rpc/ProviderApplication.java",
    "content": "package cn.iocoder.springboot.lab30.rpc;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProviderApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(ProviderApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-provider-02/src/main/java/cn/iocoder/springboot/lab30/rpc/service/UserRpcServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab30.rpc.service;\n\nimport cn.iocoder.springboot.lab30.rpc.api.UserRpcService;\nimport cn.iocoder.springboot.lab30.rpc.dto.UserDTO;\nimport org.apache.dubbo.config.annotation.Service;\n\n@Service(version = \"${dubbo.provider.UserRpcService.version}\")\npublic class UserRpcServiceImpl implements UserRpcService {\n\n    @Override\n    public UserDTO get(Integer id) {\n        return new UserDTO().setId(id)\n                .setName(\"没有昵称：\" + id)\n                .setGender(id % 2 + 1); // 1 - 男；2 - 女\n    }\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-demo/user-rpc-service-provider-02/src/main/resources/application.yaml",
    "content": "# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: user-service-provider # 应用名\n  # Dubbo 注册中心配置\n  registry:\n    address: zookeeper://127.0.0.1:2181 # 注册中心地址。配置多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 元数据中心配置\n  metadata-report:\n    address: zookeeper://127.0.0.1:2181 # 元数据中心地址。元数据中心的说明，可见 http://dubbo.apache.org/zh-cn/docs/user/references/metadata/introduction.html\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # Dubbo 服务提供者配置\n  provider:\n    timeout: 1000 # 【重要】远程服务调用超时时间，单位：毫秒。默认为 1000 毫秒，胖友可以根据自己业务修改\n    UserRpcService:\n      version: 1.0.0\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springboot.lab30.rpc.service\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-nacos/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-30</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-30-dubbo-annotations-nacos</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>user-rpc-service-api-03</module>\n        <module>user-rpc-service-provider-03</module>\n        <module>user-rpc-service-consumer-03</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-api-03/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-30-dubbo-annotations-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>user-rpc-service-api-03</artifactId>\n\n\n</project>\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-api-03/src/main/java/cn/iocoder/springboot/lab30/rpc/api/UserRpcService.java",
    "content": "package cn.iocoder.springboot.lab30.rpc.api;\n\nimport cn.iocoder.springboot.lab30.rpc.dto.UserDTO;\n\n/**\n * 用户服务 RPC Service 接口\n */\npublic interface UserRpcService {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    UserDTO get(Integer id);\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-api-03/src/main/java/cn/iocoder/springboot/lab30/rpc/dto/UserDTO.java",
    "content": "package cn.iocoder.springboot.lab30.rpc.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户信息 DTO\n */\npublic class UserDTO implements Serializable {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-consumer-03/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>user-rpc-service-consumer-03</artifactId>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>user-rpc-service-api-03</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Boot 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对 Dubbo 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n        <!-- 使用 Zookeeper 作为注册中心 -->\n<!--        <dependency>-->\n<!--            <groupId>org.apache.curator</groupId>-->\n<!--            <artifactId>curator-framework</artifactId>-->\n<!--            <version>2.13.0</version>-->\n<!--        </dependency>-->\n<!--        <dependency>-->\n<!--            <groupId>org.apache.curator</groupId>-->\n<!--            <artifactId>curator-recipes</artifactId>-->\n<!--            <version>2.13.0</version>-->\n<!--        </dependency>-->\n\n        <!-- 使用 Nacos 作为注册中心 -->\n        <dependency>\n            <groupId>com.alibaba.nacos</groupId>\n            <artifactId>nacos-client</artifactId>\n            <version>1.2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-registry-nacos</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-consumer-03/src/main/java/cn/iocoder/springboot/lab30/rpc/ConsumerApplication.java",
    "content": "package cn.iocoder.springboot.lab30.rpc;\n\nimport cn.iocoder.springboot.lab30.rpc.api.UserRpcService;\nimport cn.iocoder.springboot.lab30.rpc.dto.UserDTO;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.stereotype.Component;\n\n@SpringBootApplication\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        ConfigurableApplicationContext context = SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n    @Component\n    public class UserRpcServiceTest implements CommandLineRunner {\n\n        private final Logger logger = LoggerFactory.getLogger(getClass());\n\n        @Reference(version = \"${dubbo.consumer.UserRpcService.version}\")\n        private UserRpcService userRpcService;\n\n        @Override\n        public void run(String... args) throws Exception {\n            UserDTO user = userRpcService.get(1);\n            logger.info(\"[run][发起一次 Dubbo RPC 请求，获得用户为({})\", user);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-consumer-03/src/main/resources/application.yaml",
    "content": "# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: user-service-consumer # 应用名\n  # Dubbo 注册中心配置\n  registry:\n    address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 消费者配置\n  consumer:\n    timeout: 1000 # 【重要】远程服务调用超时时间，单位：毫秒。默认为 1000 毫秒，胖友可以根据自己业务修改\n    UserRpcService:\n      version: 1.0.0\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-provider-03/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>user-rpc-service-provider-03</artifactId>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>user-rpc-service-api-03</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Boot 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对 Dubbo 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n        <!-- 使用 Zookeeper 作为注册中心 -->\n<!--        <dependency>-->\n<!--            <groupId>org.apache.curator</groupId>-->\n<!--            <artifactId>curator-framework</artifactId>-->\n<!--            <version>2.13.0</version>-->\n<!--        </dependency>-->\n<!--        <dependency>-->\n<!--            <groupId>org.apache.curator</groupId>-->\n<!--            <artifactId>curator-recipes</artifactId>-->\n<!--            <version>2.13.0</version>-->\n<!--        </dependency>-->\n\n        <!-- 使用 Nacos 作为注册中心 -->\n        <dependency>\n            <groupId>com.alibaba.nacos</groupId>\n            <artifactId>nacos-client</artifactId>\n            <version>1.2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-registry-nacos</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-provider-03/src/main/java/cn/iocoder/springboot/lab30/rpc/ProviderApplication.java",
    "content": "package cn.iocoder.springboot.lab30.rpc;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProviderApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(ProviderApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-provider-03/src/main/java/cn/iocoder/springboot/lab30/rpc/service/UserRpcServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab30.rpc.service;\n\nimport cn.iocoder.springboot.lab30.rpc.api.UserRpcService;\nimport cn.iocoder.springboot.lab30.rpc.dto.UserDTO;\nimport org.apache.dubbo.config.annotation.Service;\n\n@Service(version = \"${dubbo.provider.UserRpcService.version}\")\npublic class UserRpcServiceImpl implements UserRpcService {\n\n    @Override\n    public UserDTO get(Integer id) {\n        return new UserDTO().setId(id)\n                .setName(\"没有昵称：\" + id)\n                .setGender(id % 2 + 1); // 1 - 男；2 - 女\n    }\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-nacos/user-rpc-service-provider-03/src/main/resources/application.yaml",
    "content": "# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: user-service-provider # 应用名\n  # Dubbo 注册中心配\n  registry:\n    address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # Dubbo 服务提供者配置\n  provider:\n    timeout: 1000 # 【重要】远程服务调用超时时间，单位：毫秒。默认为 1000 毫秒，胖友可以根据自己业务修改\n    UserRpcService:\n      version: 1.0.0\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springboot.lab30.rpc.service\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-sentinel/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-30</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-30-dubbo-annotations-sentinel</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>user-rpc-service-api-04</module>\n        <module>user-rpc-service-provider-04</module>\n        <module>user-rpc-service-consumer-04</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-api-04/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-30-dubbo-annotations-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>user-rpc-service-api-04</artifactId>\n\n\n</project>\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-api-04/src/main/java/cn/iocoder/springboot/lab30/rpc/api/UserRpcService.java",
    "content": "package cn.iocoder.springboot.lab30.rpc.api;\n\nimport cn.iocoder.springboot.lab30.rpc.dto.UserDTO;\n\n/**\n * 用户服务 RPC Service 接口\n */\npublic interface UserRpcService {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    UserDTO get(Integer id);\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-api-04/src/main/java/cn/iocoder/springboot/lab30/rpc/dto/UserDTO.java",
    "content": "package cn.iocoder.springboot.lab30.rpc.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户信息 DTO\n */\npublic class UserDTO implements Serializable {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-consumer-04/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>user-rpc-service-consumer-04</artifactId>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>user-rpc-service-api-04</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Boot 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Dubbo 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n        <!-- 使用 Zookeeper 作为注册中心 -->\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-framework</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-recipes</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n\n        <!-- Sentinel 核心库 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-core</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 接入控制台 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-transport-simple-http</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 对 Dubbo 的支持 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-apache-dubbo-adapter</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-consumer-04/src/main/java/cn/iocoder/springboot/lab30/rpc/ConsumerApplication.java",
    "content": "package cn.iocoder.springboot.lab30.rpc;\n\nimport cn.iocoder.springboot.lab30.rpc.api.UserRpcService;\nimport cn.iocoder.springboot.lab30.rpc.dto.UserDTO;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.stereotype.Component;\n\n@SpringBootApplication\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        ConfigurableApplicationContext context = SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n    @Component\n    public class UserRpcServiceTest implements CommandLineRunner {\n\n        private final Logger logger = LoggerFactory.getLogger(getClass());\n\n        @Reference(version = \"${dubbo.consumer.UserRpcService.version}\")\n        private UserRpcService userRpcService;\n\n        @Override\n        public void run(String... args) throws Exception {\n            UserDTO user = userRpcService.get(1);\n            logger.info(\"[run][发起一次 Dubbo RPC 请求，获得用户为({})\", user);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-consumer-04/src/main/java/cn/iocoder/springboot/lab30/rpc/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab30.rpc.controller;\n\nimport cn.iocoder.springboot.lab30.rpc.api.UserRpcService;\nimport cn.iocoder.springboot.lab30.rpc.dto.UserDTO;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Reference(version = \"${dubbo.consumer.UserRpcService.version}\")\n    private UserRpcService userRpcService;\n\n    @GetMapping(\"/get\")\n    public UserDTO get(@RequestParam(\"id\") Integer id) {\n        return userRpcService.get(id);\n    }\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-consumer-04/src/main/resources/application.yaml",
    "content": "# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: user-service-consumer # 应用名\n  # Dubbo 注册中心配置\n  registry:\n    address: zookeeper://127.0.0.1:2181 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 消费者配置\n  consumer:\n    timeout: 1000 # 【重要】远程服务调用超时时间，单位：毫秒。默认为 1000 毫秒，胖友可以根据自己业务修改\n    UserRpcService:\n      version: 1.0.0\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-consumer-04/src/main/resources/sentinel.properties",
    "content": "csp.sentinel.dashboard.server=127.0.0.1:7070\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-provider-04/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>user-rpc-service-provider-04</artifactId>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>user-rpc-service-api-04</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Boot 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对 Dubbo 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n        <!-- 使用 Zookeeper 作为注册中心 -->\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-framework</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-recipes</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n\n        <!-- Sentinel 核心库 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-core</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 接入控制台 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-transport-simple-http</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 对 Dubbo 的支持 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-apache-dubbo-adapter</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-provider-04/src/main/java/cn/iocoder/springboot/lab30/rpc/ProviderApplication.java",
    "content": "package cn.iocoder.springboot.lab30.rpc;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProviderApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(ProviderApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-provider-04/src/main/java/cn/iocoder/springboot/lab30/rpc/service/UserRpcServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab30.rpc.service;\n\nimport cn.iocoder.springboot.lab30.rpc.api.UserRpcService;\nimport cn.iocoder.springboot.lab30.rpc.dto.UserDTO;\nimport org.apache.dubbo.config.annotation.Service;\n\n@Service(version = \"${dubbo.provider.UserRpcService.version}\")\npublic class UserRpcServiceImpl implements UserRpcService {\n\n    @Override\n    public UserDTO get(Integer id) {\n        return new UserDTO().setId(id)\n                .setName(\"没有昵称：\" + id)\n                .setGender(id % 2 + 1); // 1 - 男；2 - 女\n    }\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-provider-04/src/main/resources/application.yaml",
    "content": "# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: user-service-provider # 应用名\n  # Dubbo 注册中心配\n  registry:\n    address: zookeeper://127.0.0.1:2181 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # Dubbo 服务提供者配置\n  provider:\n    timeout: 1000 # 【重要】远程服务调用超时时间，单位：毫秒。默认为 1000 毫秒，胖友可以根据自己业务修改\n    UserRpcService:\n      version: 1.0.0\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springboot.lab30.rpc.service\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-annotations-sentinel/user-rpc-service-provider-04/src/main/resources/sentinel.properties",
    "content": "csp.sentinel.dashboard.server=127.0.0.1:7070\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-xml-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-30</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-30-dubbo-xml-demo</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>user-rpc-service-api</module>\n        <module>user-rpc-service-provider</module>\n        <module>user-rpc-service-consumer</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-xml-demo/user-rpc-service-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-30-dubbo-xml-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>user-rpc-service-api</artifactId>\n\n    <dependencies>\n        <!-- 参数校验相关依赖 -->\n        <dependency>\n            <groupId>javax.validation</groupId>\n            <artifactId>validation-api</artifactId> <!-- JSR 参数校验规范 API -->\n            <version>2.0.1.Final</version>\n        </dependency>\n        <dependency>\n            <groupId>org.hibernate.validator</groupId>\n            <artifactId>hibernate-validator</artifactId> <!-- JSR 参数校验规范实现，我们使用 hibernate-validator -->\n            <version>6.0.18.Final</version>\n        </dependency>\n        <dependency>\n            <groupId>org.glassfish</groupId>\n            <artifactId>javax.el</artifactId> <!-- 可能涉及到 EL 表达，所以引入，否则 hibernate-validator 在初始化会报错 -->\n            <version>3.0.1-b11</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-xml-demo/user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab30/rpc/api/UserRpcService.java",
    "content": "package cn.iocoder.springboot.lab30.rpc.api;\n\nimport cn.iocoder.springboot.lab30.rpc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab30.rpc.dto.UserDTO;\n\nimport javax.validation.ConstraintViolationException;\nimport javax.validation.constraints.NotNull;\n\n/**\n * 用户服务 RPC Service 接口\n */\npublic interface UserRpcService {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    UserDTO get(@NotNull(message = \"用户编号不能为空\") Integer id)\n            throws ConstraintViolationException;\n\n    /**\n     * 添加新用户，返回新添加的用户编号\n     *\n     * @param addDTO 添加的用户信息\n     * @return 用户编号\n     */\n    Integer add(UserAddDTO addDTO)\n            throws ConstraintViolationException;\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-xml-demo/user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab30/rpc/core/ServiceException.java",
    "content": "package cn.iocoder.springboot.lab30.rpc.core;\n\n/**\n * 服务异常\n *\n * 参考 https://www.kancloud.cn/onebase/ob/484204 文章\n *\n * 一共 10 位，分成四段\n *\n * 第一段，1 位，类型\n *      1 - 业务级别异常\n *      2 - 系统级别异常\n * 第二段，3 位，系统类型\n *      001 - 用户系统\n *      002 - 商品系统\n *      003 - 订单系统\n *      004 - 支付系统\n *      005 - 优惠劵系统\n *      ... - ...\n * 第三段，3 位，模块\n *      不限制规则。\n *      一般建议，每个系统里面，可能有多个模块，可以再去做分段。以用户系统为例子：\n *          001 - OAuth2 模块\n *          002 - User 模块\n *          003 - MobileCode 模块\n * 第四段，3 位，错误码\n *       不限制规则。\n *       一般建议，每个模块自增。\n */\npublic final class ServiceException extends RuntimeException {\n\n    /**\n     * 错误码\n     */\n    private Integer code;\n\n    public ServiceException() { // 创建默认构造方法，用于反序列化的场景。\n    }\n\n    public ServiceException(ServiceExceptionEnum serviceExceptionEnum) {\n        // 使用父类的 message 字段\n        super(serviceExceptionEnum.getMessage());\n        // 设置错误码\n        this.code = serviceExceptionEnum.getCode();\n    }\n\n    public ServiceException(ServiceExceptionEnum serviceExceptionEnum, String message) {\n        // 使用父类的 message 字段\n        super(message);\n        // 设置错误码\n        this.code = serviceExceptionEnum.getCode();\n    }\n\n    public Integer getCode() {\n        return code;\n    }\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-xml-demo/user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab30/rpc/core/ServiceExceptionEnum.java",
    "content": "package cn.iocoder.springboot.lab30.rpc.core;\n\n/**\n * 业务异常枚举\n */\npublic enum ServiceExceptionEnum {\n\n    // ========== 系统级别 ==========\n    SUCCESS(0, \"成功\"),\n    SYS_ERROR(2001001000, \"服务端发生异常\"),\n    MISSING_REQUEST_PARAM_ERROR(2001001001, \"参数缺失\"),\n    INVALID_REQUEST_PARAM_ERROR(2001001002, \"请求参数不合法\"),\n\n    // ========== 用户模块 ==========\n    USER_NOT_FOUND(1001002000, \"用户不存在\"),\n    USER_EXISTS(1001002001, \"用户已存在\"),\n\n    // ========== 订单模块 ==========\n\n    // ========== 商品模块 ==========\n    ;\n\n    /**\n     * 错误码\n     */\n    private final int code;\n    /**\n     * 错误提示\n     */\n    private final String message;\n\n    ServiceExceptionEnum(int code, String message) {\n        this.code = code;\n        this.message = message;\n    }\n\n    public int getCode() {\n        return code;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-xml-demo/user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab30/rpc/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springboot.lab30.rpc.dto;\n\nimport org.hibernate.validator.constraints.Length;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO implements Serializable {\n\n    /**\n     * 昵称\n     */\n    @NotEmpty(message = \"昵称不能为空\")\n    @Length(min = 5, max = 16, message = \"账号长度为 5-16 位\")\n    private String name;\n    /**\n     * 性别\n     */\n    @NotNull(message = \"性别不能为空\")\n    private Integer gender;\n\n    public String getName() {\n        return name;\n    }\n\n    public UserAddDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserAddDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-xml-demo/user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab30/rpc/dto/UserDTO.java",
    "content": "package cn.iocoder.springboot.lab30.rpc.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户信息 DTO\n */\npublic class UserDTO implements Serializable {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-xml-demo/user-rpc-service-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>user-rpc-service-consumer</artifactId>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>user-rpc-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Boot 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对 Dubbo 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n        <!-- 使用 Zookeeper 作为注册中心 -->\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-framework</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-recipes</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-xml-demo/user-rpc-service-consumer/src/main/java/cn/iocoder/springboot/lab30/rpc/ConsumerApplication.java",
    "content": "package cn.iocoder.springboot.lab30.rpc;\n\nimport cn.iocoder.springboot.lab30.rpc.api.UserRpcService;\nimport cn.iocoder.springboot.lab30.rpc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab30.rpc.dto.UserDTO;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.ImportResource;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n@SpringBootApplication\n@ImportResource(\"classpath:dubbo.xml\")\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        ConfigurableApplicationContext context = SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n    @Component\n    public class UserRpcServiceTest implements CommandLineRunner {\n\n        private final Logger logger = LoggerFactory.getLogger(getClass());\n\n        @Resource\n        private UserRpcService userRpcService;\n\n        @Override\n        public void run(String... args) throws Exception {\n            UserDTO user = userRpcService.get(1);\n            logger.info(\"[run][发起一次 Dubbo RPC 请求，获得用户为({})]\", user);\n        }\n\n    }\n\n    @Component\n    public class UserRpcServiceTest02 implements CommandLineRunner {\n\n        private final Logger logger = LoggerFactory.getLogger(getClass());\n\n        @Resource\n        private UserRpcService userRpcService;\n\n        @Override\n        public void run(String... args) throws Exception {\n            // 获得用户\n            try {\n                // 发起调用\n                UserDTO user = userRpcService.get(null); // 故意传入空的编号，为了校验编号不通过\n                logger.info(\"[run][发起一次 Dubbo RPC 请求，获得用户为({})]\", user);\n            } catch (Exception e) {\n                logger.error(\"[run][获得用户发生异常，信息为:[{}]\", e.getMessage());\n            }\n\n            // 添加用户\n            try {\n                // 创建 UserAddDTO\n                UserAddDTO addDTO = new UserAddDTO();\n                addDTO.setName(\"yudaoyuanmayudaoyuanma\"); // 故意把名字打的特别长，为了校验名字不通过\n                addDTO.setGender(null); // 不传递性别，为了校验性别不通过\n                // 发起调用\n                userRpcService.add(addDTO);\n                logger.info(\"[run][发起一次 Dubbo RPC 请求，添加用户为({})]\", addDTO);\n            } catch (Exception e) {\n                logger.error(\"[run][添加用户发生异常，信息为:[{}]\", e.getMessage());\n            }\n        }\n\n    }\n\n    @Component\n    public class UserRpcServiceTest03 implements CommandLineRunner {\n\n        private final Logger logger = LoggerFactory.getLogger(getClass());\n\n        @Resource\n        private UserRpcService userRpcService;\n\n        @Override\n        public void run(String... args) {\n            // 添加用户\n            try {\n                // 创建 UserAddDTO\n                UserAddDTO addDTO = new UserAddDTO();\n                addDTO.setName(\"yudaoyuanma\"); // 设置为 yudaoyuanma ，触发 ServiceException 异常\n                addDTO.setGender(1);\n                // 发起调用\n                userRpcService.add(addDTO);\n                logger.info(\"[run][发起一次 Dubbo RPC 请求，添加用户为({})]\", addDTO);\n            } catch (Exception e) {\n                logger.error(\"[run][添加用户发生异常({})，信息为:[{}]\", e.getClass().getSimpleName(), e.getMessage());\n            }\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-xml-demo/user-rpc-service-consumer/src/main/resources/application.yaml",
    "content": "# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: user-service-consumer # 应用名\n  # Dubbo 注册中心配置\n  registry:\n    address: zookeeper://127.0.0.1:2181 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 消费者配置\n  consumer:\n    timeout: 1000 # 【重要】远程服务调用超时时间，单位：毫秒。默认为 1000 毫秒，胖友可以根据自己业务修改\n    UserRpcService:\n      version: 1.0.0\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-xml-demo/user-rpc-service-consumer/src/main/resources/dubbo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:dubbo=\"http://dubbo.apache.org/schema/dubbo\"\n       xmlns=\"http://www.springframework.org/schema/beans\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n       http://www.springframework.org/schema/beans/spring-beans.xsd\n       http://dubbo.apache.org/schema/dubbo\n       http://dubbo.apache.org/schema/dubbo/dubbo.xsd\">\n\n    <!-- 服务消费者引用服务配置 -->\n    <dubbo:reference id=\"userService\" interface=\"cn.iocoder.springboot.lab30.rpc.api.UserRpcService\"\n                     version=\"${dubbo.consumer.UserRpcService.version}\" validation=\"false\" />\n\n</beans>\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-xml-demo/user-rpc-service-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>user-rpc-service-provider</artifactId>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>user-rpc-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Boot 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对 Dubbo 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n        <!-- 使用 Zookeeper 作为注册中心 -->\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-framework</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-recipes</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-xml-demo/user-rpc-service-provider/src/main/java/cn/iocoder/springboot/lab30/rpc/ProviderApplication.java",
    "content": "package cn.iocoder.springboot.lab30.rpc;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.ImportResource;\n\n@SpringBootApplication\n@ImportResource(\"classpath:dubbo.xml\")\npublic class ProviderApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(ProviderApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-xml-demo/user-rpc-service-provider/src/main/java/cn/iocoder/springboot/lab30/rpc/filter/DubboExceptionFilter.java",
    "content": "package cn.iocoder.springboot.lab30.rpc.filter;\n\nimport cn.iocoder.springboot.lab30.rpc.core.ServiceException;\nimport cn.iocoder.springboot.lab30.rpc.core.ServiceExceptionEnum;\nimport org.apache.dubbo.common.constants.CommonConstants;\nimport org.apache.dubbo.common.extension.Activate;\nimport org.apache.dubbo.common.logger.Logger;\nimport org.apache.dubbo.common.logger.LoggerFactory;\nimport org.apache.dubbo.common.utils.ReflectUtils;\nimport org.apache.dubbo.common.utils.StringUtils;\nimport org.apache.dubbo.rpc.*;\nimport org.apache.dubbo.rpc.service.GenericService;\n\nimport javax.validation.ConstraintViolation;\nimport javax.validation.ConstraintViolationException;\nimport java.lang.reflect.Method;\n\n@Activate(group = CommonConstants.PROVIDER)\npublic class DubboExceptionFilter extends ListenableFilter {\n\n    public DubboExceptionFilter() {\n        super.listener = new ExceptionListenerX();\n    }\n\n    @Override\n    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {\n        return invoker.invoke(invocation);\n    }\n\n    static class ExceptionListenerX extends ExceptionListener {\n\n        @Override\n        public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {\n            // 发生异常，并且非泛化调用\n            if (appResponse.hasException() && GenericService.class != invoker.getInterface()) {\n                Throwable exception = appResponse.getException();\n                // 如果是 ServiceException 异常，直接返回\n                if (exception instanceof ServiceException) {\n                    return;\n                }\n                // 如果是参数校验的 ConstraintViolationException 异常，则封装返回\n                if (exception instanceof ConstraintViolationException) {\n                    appResponse.setException(this.handleConstraintViolationException((ConstraintViolationException) exception));\n                    return;\n                }\n            }\n            // 其它情况，继续使用父类处理\n            super.onResponse(appResponse, invoker, invocation);\n        }\n\n        private ServiceException handleConstraintViolationException(ConstraintViolationException ex) {\n            // 拼接错误\n            StringBuilder detailMessage = new StringBuilder();\n            for (ConstraintViolation<?> constraintViolation : ex.getConstraintViolations()) {\n                // 使用 ; 分隔多个错误\n                if (detailMessage.length() > 0) {\n                    detailMessage.append(\";\");\n                }\n                // 拼接内容到其中\n                detailMessage.append(constraintViolation.getMessage());\n            }\n            // 返回异常\n            return new ServiceException(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR,\n                    detailMessage.toString());\n        }\n\n    }\n\n    static class ExceptionListener implements Listener {\n\n        private Logger logger = LoggerFactory.getLogger(ExceptionListener.class);\n\n        @Override\n        public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {\n            if (appResponse.hasException() && GenericService.class != invoker.getInterface()) {\n                try {\n                    Throwable exception = appResponse.getException();\n\n                    // directly throw if it's checked exception\n                    if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) {\n                        return;\n                    }\n                    // directly throw if the exception appears in the signature\n                    try {\n                        Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());\n                        Class<?>[] exceptionClassses = method.getExceptionTypes();\n                        for (Class<?> exceptionClass : exceptionClassses) {\n                            if (exception.getClass().equals(exceptionClass)) {\n                                return;\n                            }\n                        }\n                    } catch (NoSuchMethodException e) {\n                        return;\n                    }\n\n                    // for the exception not found in method's signature, print ERROR message in server's log.\n                    logger.error(\"Got unchecked and undeclared exception which called by \" + RpcContext.getContext().getRemoteHost() + \". service: \" + invoker.getInterface().getName() + \", method: \" + invocation.getMethodName() + \", exception: \" + exception.getClass().getName() + \": \" + exception.getMessage(), exception);\n\n                    // directly throw if exception class and interface class are in the same jar file.\n                    String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());\n                    String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());\n                    if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) {\n                        return;\n                    }\n                    // directly throw if it's JDK exception\n                    String className = exception.getClass().getName();\n                    if (className.startsWith(\"java.\") || className.startsWith(\"javax.\")) {\n                        return;\n                    }\n                    // directly throw if it's dubbo exception\n                    if (exception instanceof RpcException) {\n                        return;\n                    }\n\n                    // otherwise, wrap with RuntimeException and throw back to the client\n                    appResponse.setException(new RuntimeException(StringUtils.toString(exception)));\n                    return;\n                } catch (Throwable e) {\n                    logger.warn(\"Fail to ExceptionFilter when called by \" + RpcContext.getContext().getRemoteHost() + \". service: \" + invoker.getInterface().getName() + \", method: \" + invocation.getMethodName() + \", exception: \" + e.getClass().getName() + \": \" + e.getMessage(), e);\n                    return;\n                }\n            }\n        }\n\n        @Override\n        public void onError(Throwable e, Invoker<?> invoker, Invocation invocation) {\n            logger.error(\"Got unchecked and undeclared exception which called by \" + RpcContext.getContext().getRemoteHost() + \". service: \" + invoker.getInterface().getName() + \", method: \" + invocation.getMethodName() + \", exception: \" + e.getClass().getName() + \": \" + e.getMessage(), e);\n        }\n\n        // For test purpose\n        public void setLogger(Logger logger) {\n            this.logger = logger;\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-xml-demo/user-rpc-service-provider/src/main/java/cn/iocoder/springboot/lab30/rpc/service/UserRpcServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab30.rpc.service;\n\n\nimport cn.iocoder.springboot.lab30.rpc.api.UserRpcService;\nimport cn.iocoder.springboot.lab30.rpc.core.ServiceException;\nimport cn.iocoder.springboot.lab30.rpc.core.ServiceExceptionEnum;\nimport cn.iocoder.springboot.lab30.rpc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab30.rpc.dto.UserDTO;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class UserRpcServiceImpl implements UserRpcService {\n\n    @Override\n    public UserDTO get(Integer id) {\n        return new UserDTO().setId(id)\n                .setName(\"没有昵称：\" + id)\n                .setGender(id % 2 + 1); // 1 - 男；2 - 女\n    }\n\n    @Override\n    public Integer add(UserAddDTO addDTO) {\n        // 这里，模拟用户已经存在的情况\n        if (\"yudaoyuanma\".equals(addDTO.getName())) {\n            throw new ServiceException(ServiceExceptionEnum.USER_EXISTS);\n        }\n        return (int) (System.currentTimeMillis() / 1000); // 嘿嘿，随便返回一个 id\n    }\n\n}\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-xml-demo/user-rpc-service-provider/src/main/resources/META-INF/dubbo/org.apache.dubbo.rpc.Filter",
    "content": "dubboExceptionFilter=cn.iocoder.springboot.lab30.rpc.filter.DubboExceptionFilter\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-xml-demo/user-rpc-service-provider/src/main/resources/application.yaml",
    "content": "# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: user-service-provider # 应用名\n  # Dubbo 注册中心配\n  registry:\n    address: zookeeper://127.0.0.1:2181 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # Dubbo 服务提供者配置\n  provider:\n    timeout: 1000 # 【重要】远程服务调用超时时间，单位：毫秒。默认为 1000 毫秒，胖友可以根据自己业务修改\n    filter: -exception # 去掉 ExceptionFilter\n    UserRpcService:\n      version: 1.0.0\n"
  },
  {
    "path": "lab-30/lab-30-dubbo-xml-demo/user-rpc-service-provider/src/main/resources/dubbo.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:dubbo=\"http://dubbo.apache.org/schema/dubbo\"\n       xmlns=\"http://www.springframework.org/schema/beans\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans\n       http://www.springframework.org/schema/beans/spring-beans.xsd\n       http://dubbo.apache.org/schema/dubbo\n       http://dubbo.apache.org/schema/dubbo/dubbo.xsd\">\n\n    <!-- 服务提供者暴露服务配置 -->\n    <dubbo:service ref=\"userRpcServiceImpl\" interface=\"cn.iocoder.springboot.lab30.rpc.api.UserRpcService\"\n        version=\"${dubbo.provider.UserRpcService.version}\" validation=\"true\" />\n\n</beans>\n"
  },
  {
    "path": "lab-30/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-30</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-30-dubbo-xml-demo</module>\n        <module>lab-30-dubbo-annotations-demo</module>\n        <module>lab-30-dubbo-annotations-nacos</module>\n        <module>lab-30-dubbo-annotations-sentinel</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-30/《芋道 Dubbo Admin 快速入门》.md",
    "content": "<http://www.iocoder.cn/Dubbo/Admin/?github>\n"
  },
  {
    "path": "lab-30/《芋道 Dubbo Swagger 快速入门》.md",
    "content": "<http://www.iocoder.cn/Dubbo/Swagger/?github>\n"
  },
  {
    "path": "lab-30/《芋道 Spring Boot Dubbo 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Dubbo/?github>\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-31-rocketmq-demo</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RocketMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.rocketmq</groupId>\n            <artifactId>rocketmq-spring-boot-starter</artifactId>\n            <version>2.0.4</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/consumer/Demo01AConsumer.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo01Message;\nimport org.apache.rocketmq.common.message.MessageExt;\nimport org.apache.rocketmq.spring.annotation.RocketMQMessageListener;\nimport org.apache.rocketmq.spring.core.RocketMQListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RocketMQMessageListener(\n        topic = Demo01Message.TOPIC,\n        consumerGroup = \"demo01-A-consumer-group-\" + Demo01Message.TOPIC\n)\npublic class Demo01AConsumer implements RocketMQListener<MessageExt> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void onMessage(MessageExt message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/consumer/Demo01Consumer.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo01Message;\nimport org.apache.rocketmq.spring.annotation.RocketMQMessageListener;\nimport org.apache.rocketmq.spring.core.RocketMQListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RocketMQMessageListener(\n        topic = Demo01Message.TOPIC,\n        consumerGroup = \"demo01-consumer-group-\" + Demo01Message.TOPIC\n)\npublic class Demo01Consumer implements RocketMQListener<Demo01Message> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void onMessage(Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/consumer/Demo02Consumer.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo02Message;\nimport org.apache.rocketmq.spring.core.RocketMQListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\n//@RocketMQMessageListener(\n//        topic = Demo02Message.TOPIC,\n//        consumerGroup = \"demo02-consumer-group-\" + Demo02Message.TOPIC\n//)\npublic class Demo02Consumer implements RocketMQListener<Demo02Message> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void onMessage(Demo02Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/consumer/Demo03Consumer.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo03Message;\nimport org.apache.rocketmq.spring.annotation.RocketMQMessageListener;\nimport org.apache.rocketmq.spring.core.RocketMQListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RocketMQMessageListener(\n        topic = Demo03Message.TOPIC,\n        consumerGroup = \"demo03-consumer-group-\" + Demo03Message.TOPIC\n)\npublic class Demo03Consumer implements RocketMQListener<Demo03Message> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void onMessage(Demo03Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/consumer/Demo04Consumer.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo04Message;\nimport org.apache.rocketmq.spring.annotation.RocketMQMessageListener;\nimport org.apache.rocketmq.spring.core.RocketMQListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RocketMQMessageListener(\n        topic = Demo04Message.TOPIC,\n        consumerGroup = \"demo04-consumer-group-\" + Demo04Message.TOPIC\n)\npublic class Demo04Consumer implements RocketMQListener<Demo04Message> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void onMessage(Demo04Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n        // 注意，此处抛出一个 RuntimeException 异常，模拟消费失败\n        throw new RuntimeException(\"我就是故意抛出一个异常\");\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/consumer/Demo05Consumer.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo05Message;\nimport org.apache.rocketmq.spring.annotation.MessageModel;\nimport org.apache.rocketmq.spring.annotation.RocketMQMessageListener;\nimport org.apache.rocketmq.spring.core.RocketMQListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RocketMQMessageListener(\n        topic = Demo05Message.TOPIC,\n        consumerGroup = \"demo05-consumer-group-\" + Demo05Message.TOPIC,\n        messageModel = MessageModel.BROADCASTING // 设置为广播消费\n)\npublic class Demo05Consumer implements RocketMQListener<Demo05Message> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void onMessage(Demo05Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/consumer/Demo06Consumer.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo06Message;\nimport org.apache.rocketmq.spring.annotation.ConsumeMode;\nimport org.apache.rocketmq.spring.annotation.RocketMQMessageListener;\nimport org.apache.rocketmq.spring.core.RocketMQListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RocketMQMessageListener(\n        topic = Demo06Message.TOPIC,\n        consumerGroup = \"demo06-consumer-group-\" + Demo06Message.TOPIC,\n        consumeMode = ConsumeMode.ORDERLY // 设置为顺序消费\n)\npublic class Demo06Consumer implements RocketMQListener<Demo06Message> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void onMessage(Demo06Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n\n        // sleep 2 秒，用于查看顺序消费的效果\n        try {\n            Thread.sleep(2 * 1000L);\n        } catch (InterruptedException ignore) {\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/consumer/Demo07Consumer.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo07Message;\nimport org.apache.rocketmq.spring.annotation.RocketMQMessageListener;\nimport org.apache.rocketmq.spring.core.RocketMQListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RocketMQMessageListener(\n        topic = Demo07Message.TOPIC,\n        consumerGroup = \"demo07-consumer-group-\" + Demo07Message.TOPIC\n)\npublic class Demo07Consumer implements RocketMQListener<Demo07Message> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void onMessage(Demo07Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/core/ExtRocketMQTemplate.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.core;\n\nimport org.apache.rocketmq.spring.annotation.ExtRocketMQTemplateConfiguration;\nimport org.apache.rocketmq.spring.core.RocketMQTemplate;\n\n@ExtRocketMQTemplateConfiguration(nameServer = \"${demo.rocketmq.extNameServer:demo.rocketmq.name-server}\")\npublic class ExtRocketMQTemplate extends RocketMQTemplate {\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    public static final String TOPIC = \"DEMO_01\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/message/Demo02Message.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.message;\n\n/**\n * 示例 02 的 Message 消息\n */\npublic class Demo02Message {\n\n    public static final String TOPIC = \"DEMO_02\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo02Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo02Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/message/Demo03Message.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.message;\n\n/**\n * 示例 03 的 Message 消息\n */\npublic class Demo03Message {\n\n    public static final String TOPIC = \"DEMO_03\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo03Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo03Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/message/Demo04Message.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.message;\n\n/**\n * 示例 04 的 Message 消息\n */\npublic class Demo04Message {\n\n    public static final String TOPIC = \"DEMO_04\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo04Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo04Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/message/Demo05Message.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.message;\n\n/**\n * 示例 05 的 Message 消息\n */\npublic class Demo05Message {\n\n    public static final String TOPIC = \"DEMO_05\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo05Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo05Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/message/Demo06Message.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.message;\n\n/**\n * 示例 06 的 Message 消息\n */\npublic class Demo06Message {\n\n    public static final String TOPIC = \"DEMO_06\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo06Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo06Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/message/Demo07Message.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.message;\n\n/**\n * 示例 07 的 Message 消息\n */\npublic class Demo07Message {\n\n    public static final String TOPIC = \"DEMO_07\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo07Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo07Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo01Producer.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.producer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo01Message;\nimport org.apache.rocketmq.client.producer.SendCallback;\nimport org.apache.rocketmq.client.producer.SendResult;\nimport org.apache.rocketmq.spring.core.RocketMQTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Producer {\n\n    @Autowired\n    private RocketMQTemplate rocketMQTemplate;\n\n    public SendResult syncSend(Integer id) {\n        // 创建 Demo01Message 消息\n        Demo01Message message = new Demo01Message();\n        message.setId(id);\n        // 同步发送消息\n        return rocketMQTemplate.syncSend(Demo01Message.TOPIC, message);\n    }\n\n    public void asyncSend(Integer id, SendCallback callback) {\n        // 创建 Demo01Message 消息\n        Demo01Message message = new Demo01Message();\n        message.setId(id);\n        // 异步发送消息\n        rocketMQTemplate.asyncSend(Demo01Message.TOPIC, message, callback);\n    }\n\n    public void onewaySend(Integer id) {\n        // 创建 Demo01Message 消息\n        Demo01Message message = new Demo01Message();\n        message.setId(id);\n        // oneway 发送消息\n        rocketMQTemplate.sendOneWay(Demo01Message.TOPIC, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo02Producer.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.producer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo02Message;\nimport org.apache.rocketmq.client.producer.SendResult;\nimport org.apache.rocketmq.spring.core.RocketMQTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\n@Component\npublic class Demo02Producer {\n\n    @Autowired\n    private RocketMQTemplate rocketMQTemplate;\n\n    public SendResult sendBatch(Collection<Integer> ids) {\n        // 创建多条 Demo02Message 消息\n        List<Message> messages = new ArrayList<>(ids.size());\n        for (Integer id : ids) {\n            // 创建 Demo02Message 消息\n            Demo02Message message = new Demo02Message().setId(id);\n            // 构建 Spring Messaging 定义的 Message 消息\n            messages.add(MessageBuilder.withPayload(message).build());\n        }\n        // 同步批量发送消息\n        return rocketMQTemplate.syncSend(Demo02Message.TOPIC, messages, 30 * 1000L);\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo03Producer.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.producer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo03Message;\nimport org.apache.rocketmq.client.producer.SendCallback;\nimport org.apache.rocketmq.client.producer.SendResult;\nimport org.apache.rocketmq.spring.core.RocketMQTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo03Producer {\n\n    @Autowired\n    private RocketMQTemplate rocketMQTemplate;\n\n    public SendResult syncSendDelay(Integer id, int delayLevel) {\n        // 创建 Demo03Message 消息\n        Message message = MessageBuilder.withPayload(new Demo03Message().setId(id))\n                .build();\n        // 同步发送消息\n        return rocketMQTemplate.syncSend(Demo03Message.TOPIC, message, 30 * 1000,\n                delayLevel);\n    }\n\n    public void asyncSendDelay(Integer id, int delayLevel, SendCallback callback) {\n        // 创建 Demo03Message 消息\n        Message message = MessageBuilder.withPayload(new Demo03Message().setId(id))\n                .build();\n        // 同步发送消息\n        rocketMQTemplate.asyncSend(Demo03Message.TOPIC, message, callback, 30 * 1000,\n                delayLevel);\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo04Producer.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.producer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo04Message;\nimport org.apache.rocketmq.client.producer.SendResult;\nimport org.apache.rocketmq.spring.core.RocketMQTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo04Producer {\n\n    @Autowired\n    private RocketMQTemplate rocketMQTemplate;\n\n    public SendResult syncSend(Integer id) {\n        // 创建 Demo04Message 消息\n        Demo04Message message = new Demo04Message();\n        message.setId(id);\n        // 同步发送消息\n        return rocketMQTemplate.syncSend(Demo04Message.TOPIC, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo05Producer.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.producer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo05Message;\nimport org.apache.rocketmq.client.producer.SendResult;\nimport org.apache.rocketmq.spring.core.RocketMQTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo05Producer {\n\n    @Autowired\n    private RocketMQTemplate rocketMQTemplate;\n\n    public SendResult syncSend(Integer id) {\n        // 创建 Demo05Message 消息\n        Demo05Message message = new Demo05Message();\n        message.setId(id);\n        // 同步发送消息\n        return rocketMQTemplate.syncSend(Demo05Message.TOPIC, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo06Producer.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.producer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo06Message;\nimport org.apache.rocketmq.client.producer.SendCallback;\nimport org.apache.rocketmq.client.producer.SendResult;\nimport org.apache.rocketmq.spring.core.RocketMQTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo06Producer {\n\n    @Autowired\n    private RocketMQTemplate rocketMQTemplate;\n\n    public SendResult syncSendOrderly(Integer id) {\n        // 创建 Demo06Message 消息\n        Demo06Message message = new Demo06Message();\n        message.setId(id);\n        // 同步发送消息\n        return rocketMQTemplate.syncSendOrderly(Demo06Message.TOPIC, message, String.valueOf(id));\n    }\n\n    public void asyncSendOrderly(Integer id, SendCallback callback) {\n        // 创建 Demo06Message 消息\n        Demo06Message message = new Demo06Message();\n        message.setId(id);\n        // 异步发送消息\n        rocketMQTemplate.asyncSendOrderly(Demo06Message.TOPIC, message, String.valueOf(id), callback);\n    }\n\n    public void onewaySendOrderly(Integer id) {\n        // 创建 Demo06Message 消息\n        Demo06Message message = new Demo06Message();\n        message.setId(id);\n        // 异步发送消息\n        rocketMQTemplate.sendOneWayOrderly(Demo06Message.TOPIC, message, String.valueOf(id));\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo07Producer.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.producer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo07Message;\nimport org.apache.rocketmq.client.producer.TransactionSendResult;\nimport org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;\nimport org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;\nimport org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;\nimport org.apache.rocketmq.spring.core.RocketMQTemplate;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo07Producer {\n\n    private static final String TX_PRODUCER_GROUP = \"demo07-producer-group\";\n\n    @Autowired\n    private RocketMQTemplate rocketMQTemplate;\n\n    public TransactionSendResult sendMessageInTransaction(Integer id) {\n        // 创建 Demo07Message 消息\n        Message<Demo07Message> message = MessageBuilder.withPayload(new Demo07Message().setId(id))\n                .build();\n        // 发送事务消息\n        return rocketMQTemplate.sendMessageInTransaction(TX_PRODUCER_GROUP, Demo07Message.TOPIC, message,\n                id);\n    }\n\n    @RocketMQTransactionListener(txProducerGroup = TX_PRODUCER_GROUP)\n    public class TransactionListenerImpl implements RocketMQLocalTransactionListener {\n\n        private Logger logger = LoggerFactory.getLogger(getClass());\n\n        @Override\n        public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {\n            // ... local transaction process, return rollback, commit or unknown\n            logger.info(\"[executeLocalTransaction][执行本地事务，消息：{} arg：{}]\", msg, arg);\n            return RocketMQLocalTransactionState.UNKNOWN;\n        }\n\n        @Override\n        public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {\n            // ... check transaction status and return rollback, commit or unknown\n            logger.info(\"[checkLocalTransaction][回查消息：{}]\", msg);\n            return RocketMQLocalTransactionState.COMMIT;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/main/resources/application.yaml",
    "content": "# rocketmq 配置项，对应 RocketMQProperties 配置类\nrocketmq:\n  name-server: 127.0.0.1:9876 # RocketMQ Namesrv\n  # Producer 配置项\n  producer:\n    group: demo-producer-group # 生产者分组\n    send-message-timeout: 3000 # 发送消息超时时间，单位：毫秒。默认为 3000 。\n    compress-message-body-threshold: 4096 # 消息压缩阀值，当消息体的大小超过该阀值后，进行消息压缩。默认为 4 * 1024B\n    max-message-size: 4194304 # 消息体的最大允许大小。。默认为 4 * 1024 * 1024B\n    retry-times-when-send-failed: 2 # 同步发送消息时，失败重试次数。默认为 2 次。\n    retry-times-when-send-async-failed: 2 # 异步发送消息时，失败重试次数。默认为 2 次。\n    retry-next-server: false # 发送消息给 Broker 时，如果发送失败，是否重试另外一台 Broker 。默认为 false\n    access-key: # Access Key ，可阅读 https://github.com/apache/rocketmq/blob/master/docs/cn/acl/user_guide.md 文档\n    secret-key: # Secret Key\n    enable-msg-trace: true # 是否开启消息轨迹功能。默认为 true 开启。可阅读 https://github.com/apache/rocketmq/blob/master/docs/cn/msg_trace/user_guide.md 文档\n    customized-trace-topic: RMQ_SYS_TRACE_TOPIC # 自定义消息轨迹的 Topic 。默认为 RMQ_SYS_TRACE_TOPIC 。\n  # Consumer 配置项\n  consumer:\n    listeners: # 配置某个消费分组，是否监听指定 Topic 。结构为 Map<消费者分组, <Topic, Boolean>> 。默认情况下，不配置表示监听。\n      test-consumer-group:\n        topic1: false # 关闭 test-consumer-group 对 topic1 的监听消费\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo;\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo01ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.producer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.Application;\nimport org.apache.rocketmq.client.producer.SendCallback;\nimport org.apache.rocketmq.client.producer.SendResult;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo01ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo01Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        SendResult result = producer.syncSend(id);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送结果：[{}]]\", id, result);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testASyncSend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.asyncSend(id, new SendCallback() {\n\n            @Override\n            public void onSuccess(SendResult result) {\n                logger.info(\"[testASyncSend][发送编号：[{}] 发送成功，结果为：[{}]]\", id, result);\n            }\n\n            @Override\n            public void onException(Throwable e) {\n                logger.info(\"[testASyncSend][发送编号：[{}] 发送异常]]\", id, e);\n            }\n\n        });\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testOnewaySend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.onewaySend(id);\n        logger.info(\"[testOnewaySend][发送编号：[{}] 发送完成]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo02ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.producer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.Application;\nimport org.apache.rocketmq.client.producer.SendResult;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo02ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo02Producer producer;\n\n    @Test\n    public void testSendBatch() throws InterruptedException {\n        List<Integer> ids = Arrays.asList(1, 2, 3);\n        SendResult result = producer.sendBatch(ids);\n        logger.info(\"[testSendBatch][发送编号：[{}] 发送结果：[{}]]\", ids, result);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo03ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.producer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.Application;\nimport org.apache.rocketmq.client.producer.SendResult;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo03ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo03Producer producer;\n\n    @Test\n    public void testSyncSendDelay() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        SendResult result = producer.syncSendDelay(id, 3); // 延迟级别 3 ，即 10 秒后消费\n        logger.info(\"[testSyncSendDelay][发送编号：[{}] 发送结果：[{}]]\", id, result);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo04ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.producer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.Application;\nimport org.apache.rocketmq.client.producer.SendResult;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo04ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo04Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        SendResult result = producer.syncSend(id);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送结果：[{}]]\", id, result);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo05ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.producer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.Application;\nimport org.apache.rocketmq.client.producer.SendResult;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo05ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo05Producer producer;\n\n    @Test\n    public void test() throws InterruptedException {\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        SendResult result = producer.syncSend(id);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送结果：[{}]]\", id, result);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo06ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.producer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.Application;\nimport org.apache.rocketmq.client.producer.SendCallback;\nimport org.apache.rocketmq.client.producer.SendResult;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo06ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo06Producer producer;\n\n    @Test\n    public void testSyncSendOrderly() throws InterruptedException {\n        // 发送多条消息\n        for (int i = 0; i < 3; i++) {\n            int id = 1024; // 固定成 1024 ，方便我们测试是否发送到相同消息队列\n            SendResult result = producer.syncSendOrderly(id);\n            logger.info(\"[testSyncSendOrderly][发送编号：[{}] 发送结果：[{}]]\", id, result);\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testASyncSendOrderly() throws InterruptedException {\n        for (int i = 0; i < 3; i++) {\n            int id = 1024; // 固定成 1024 ，方便我们测试是否发送到相同消息队列\n            producer.asyncSendOrderly(id, new SendCallback() {\n\n                @Override\n                public void onSuccess(SendResult result) {\n                    logger.info(\"[testASyncSendOrderly][发送编号：[{}] 发送成功，结果为：[{}]]\", id, result);\n                }\n\n                @Override\n                public void onException(Throwable e) {\n                    logger.info(\"[testASyncSendOrderly][发送编号：[{}] 发送异常]]\", id, e);\n                }\n\n            });\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testOnewaySendOrderly() throws InterruptedException {\n        for (int i = 0; i < 3; i++) {\n            int id = 1024; // 固定成 1024 ，方便我们测试是否发送到相同消息队列\n            producer.onewaySendOrderly(id);\n            logger.info(\"[testOnewaySendOrderly][发送编号：[{}] 发送完成]\", id);\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-demo/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo07ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.producer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.Application;\nimport org.apache.rocketmq.client.producer.SendResult;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo07ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo07Producer producer;\n\n    @Test\n    public void testSendMessageInTransaction() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        SendResult result = producer.sendMessageInTransaction(id);\n        logger.info(\"[testSendMessageInTransaction][发送编号：[{}] 发送结果：[{}]]\", id, result);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-ons/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-31-rocketmq-ons</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RocketMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.rocketmq</groupId>\n            <artifactId>rocketmq-spring-boot-starter</artifactId>\n            <version>2.0.4</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-ons/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-ons/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/consumer/Demo01Consumer.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo01Message;\nimport org.apache.rocketmq.spring.annotation.RocketMQMessageListener;\nimport org.apache.rocketmq.spring.core.RocketMQListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RocketMQMessageListener(\n        topic = Demo01Message.TOPIC,\n        consumerGroup = \"GID_CONSUMER_GROUP_YUNAI_TEST\"\n)\npublic class Demo01Consumer implements RocketMQListener<Demo01Message> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void onMessage(Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-ons/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    public static final String TOPIC = \"TOPIC_YUNAI_TEST\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-ons/src/main/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo01Producer.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.producer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.message.Demo01Message;\nimport org.apache.rocketmq.client.producer.SendCallback;\nimport org.apache.rocketmq.client.producer.SendResult;\nimport org.apache.rocketmq.spring.core.RocketMQTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Producer {\n\n    @Autowired\n    private RocketMQTemplate rocketMQTemplate;\n\n    public SendResult syncSend(Integer id) {\n        // 创建 Demo01Message 消息\n        Demo01Message message = new Demo01Message();\n        message.setId(id);\n        // 同步发送消息\n        return rocketMQTemplate.syncSend(Demo01Message.TOPIC, message);\n    }\n\n    public void asyncSend(Integer id, SendCallback callback) {\n        // 创建 Demo01Message 消息\n        Demo01Message message = new Demo01Message();\n        message.setId(id);\n        // 异步发送消息\n        rocketMQTemplate.asyncSend(Demo01Message.TOPIC, message, callback);\n    }\n\n    public void onewaySend(Integer id) {\n        // 创建 Demo01Message 消息\n        Demo01Message message = new Demo01Message();\n        message.setId(id);\n        // oneway 发送消息\n        rocketMQTemplate.sendOneWay(Demo01Message.TOPIC, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-ons/src/main/resources/application.yaml",
    "content": "# rocketmq 配置项，对应 RocketMQProperties 配置类\nrocketmq:\n  name-server: http://onsaddr.mq-internet-access.mq-internet.aliyuncs.com:80 # 阿里云 RocketMQ Namesrv\n  access-channel: CLOUD # 设置使用阿里云\n  # Producer 配置项\n  producer:\n    group: GID_PRODUCER_GROUP_YUNAI_TEST # 生产者分组\n    access-key: # 设置阿里云的 RocketMQ 的 access key ！！！这里涉及到隐私，所以这里艿艿没有提供\n    secret-key: # 设置阿里云的 RocketMQ 的 secret key ！！！这里涉及到隐私，所以这里艿艿没有提供\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-ons/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo;\n"
  },
  {
    "path": "lab-31/lab-31-rocketmq-ons/src/test/java/cn/iocoder/springboot/lab31/rocketmqdemo/producer/Demo01ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab31.rocketmqdemo.producer;\n\nimport cn.iocoder.springboot.lab31.rocketmqdemo.Application;\nimport org.apache.rocketmq.client.producer.SendCallback;\nimport org.apache.rocketmq.client.producer.SendResult;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo01ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo01Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        SendResult result = producer.syncSend(id);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送结果：[{}]]\", id, result);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testASyncSend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.asyncSend(id, new SendCallback() {\n\n            @Override\n            public void onSuccess(SendResult result) {\n                logger.info(\"[testASyncSend][发送编号：[{}] 发送成功，结果为：[{}]]\", id, result);\n            }\n\n            @Override\n            public void onException(Throwable e) {\n                logger.info(\"[testASyncSend][发送编号：[{}] 发送异常]]\", id, e);\n            }\n\n        });\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testOnewaySend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.onewaySend(id);\n        logger.info(\"[testOnewaySend][发送编号：[{}] 发送完成]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-31/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-31</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-31-rocketmq-demo</module>\n        <module>lab-31-rocketmq-ons</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-31/《芋道 Spring Boot 消息队列 RocketMQ 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/RocketMQ/?github>\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-32-activemq-demo</artifactId>\n\n    <dependencies>\n        <!-- 实现对 ActiveMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-activemq</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.scheduling.annotation.EnableAsync;\n\n@SpringBootApplication\n@EnableAsync // 开启异步\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/consumer/Demo01Consumer.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.consumer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jms.annotation.JmsListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @JmsListener(destination = Demo01Message.QUEUE)\n    public void onMessage(Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n//    @JmsListener(destination = Demo01Message.QUEUE)\n//    public void onMessage(javax.jms.Message message) {\n//        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n//    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo01Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_01\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo01Producer.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.producer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.message.Demo01Message;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jms.core.JmsMessagingTemplate;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.scheduling.annotation.AsyncResult;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.concurrent.ListenableFuture;\n\n@Component\npublic class Demo01Producer {\n\n    @Autowired\n    private JmsMessagingTemplate jmsTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 ClusteringMessage 消息\n        Demo01Message message = new Demo01Message();\n        message.setId(id);\n        // 同步发送消息\n        jmsTemplate.convertAndSend(Demo01Message.QUEUE, message);\n    }\n\n    @Async\n    public ListenableFuture<Void> asyncSend(Integer id) {\n        try {\n            // 发送消息\n            this.syncSend(id);\n            // 返回成功的 Future\n            return AsyncResult.forValue(null);\n        } catch (Throwable ex) {\n            // 返回异常的 Future\n            return AsyncResult.forExecutionException(ex);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo/src/main/resources/application.yaml",
    "content": "spring:\n  # ActiveMQ 配置项，对应 ActiveMQProperties 配置类\n  activemq:\n    broker-url: tcp://127.0.0.1:61616 # ActiveMQ Broker 的地址\n    user: admin # 账号\n    password: admin # 密码\n    packages:\n      trust-all: true # 可信任的反序列化包\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo;\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo01ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.producer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.util.concurrent.ListenableFutureCallback;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo01ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo01Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        // 发送消息\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSend(id);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testAsyncSend() throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.asyncSend(id).addCallback(new ListenableFutureCallback<Void>() {\n\n            @Override\n            public void onFailure(Throwable e) {\n                logger.info(\"[testASyncSend][发送编号：[{}] 发送异常]]\", id, e);\n            }\n\n            @Override\n            public void onSuccess(Void aVoid) {\n                logger.info(\"[testASyncSend][发送编号：[{}] 发送成功，发送成功]\", id);\n            }\n\n        });\n        logger.info(\"[testASyncSend][发送编号：[{}] 调用完成]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-concurrency/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-32-activemq-demo-concurrency</artifactId>\n\n    <dependencies>\n        <!-- 实现对 ActiveMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-activemq</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-concurrency/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-concurrency/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/consumer/Demo03Consumer.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.consumer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.message.Demo03Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jms.annotation.JmsListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo03Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @JmsListener(destination = Demo03Message.QUEUE,\n        concurrency = \"2\")\n    public void onMessage(Demo03Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-concurrency/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/message/Demo03Message.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo03Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_03\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo03Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo03Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-concurrency/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo03Producer.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.producer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.message.Demo03Message;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jms.core.JmsMessagingTemplate;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo03Producer {\n\n    @Autowired\n    private JmsMessagingTemplate jmsTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 Demo03Message 消息\n        Demo03Message message = new Demo03Message();\n        message.setId(id);\n        // 同步发送消息\n        jmsTemplate.convertAndSend(Demo03Message.QUEUE, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-concurrency/src/main/resources/application.yaml",
    "content": "spring:\n  # ActiveMQ 配置项，对应 ActiveMQProperties 配置类\n  activemq:\n    broker-url: tcp://127.0.0.1:61616 # ActiveMQ Broker 的地址\n    user: admin # 账号\n    password: admin # 密码\n    packages:\n      trust-all: true # 可信任的反序列化包\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-concurrency/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo;\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-concurrency/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo03ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.producer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo03ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo03Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        for (int i = 0; i < 10; i++) {\n            int id = (int) (System.currentTimeMillis() / 1000);\n            producer.syncSend(id);\n//            logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-consume-retry/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-32-activemq-demo-consume-retry</artifactId>\n\n    <dependencies>\n        <!-- 实现对 ActiveMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-activemq</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-consume-retry/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-consume-retry/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/config/ActiveMQConfig.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.config;\n\nimport org.apache.activemq.ActiveMQConnectionFactory;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot.autoconfigure.jms.activemq.ActiveMQConnectionFactoryCustomizer;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class ActiveMQConfig implements ActiveMQConnectionFactoryCustomizer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void customize(ActiveMQConnectionFactory factory) {\n        logger.info(\"[customize][默认重试策略: {}]\", factory.getRedeliveryPolicy());\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-consume-retry/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/consumer/Demo05Consumer.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.consumer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.message.Demo05Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jms.annotation.JmsListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo05Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @JmsListener(destination = Demo05Message.QUEUE)\n    public void onMessage(Demo05Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n        // <X> 注意，此处抛出一个 RuntimeException 异常，模拟消费失败\n        throw new RuntimeException(\"我就是故意抛出一个异常\");\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-consume-retry/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/message/Demo05Message.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo05Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_05\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo05Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo05Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-consume-retry/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo05Producer.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.producer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.message.Demo05Message;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jms.core.JmsMessagingTemplate;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo05Producer {\n\n    @Autowired\n    private JmsMessagingTemplate jmsTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 ClusteringMessage 消息\n        Demo05Message message = new Demo05Message();\n        message.setId(id);\n        // 同步发送消息\n        jmsTemplate.convertAndSend(Demo05Message.QUEUE, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-consume-retry/src/main/resources/application.yaml",
    "content": "spring:\n  # ActiveMQ 配置项，对应 ActiveMQProperties 配置类\n  activemq:\n    broker-url: tcp://127.0.0.1:61616 # ActiveMQ Broker 的地址\n    user: admin # 账号\n    password: admin # 密码\n    packages:\n      trust-all: true # 可信任的反序列化包\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-consume-retry/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo;\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-consume-retry/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo05ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.producer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo05ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo05Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        // 发送消息\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSend(id);\n        logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-delay/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-32-activemq-demo-delay</artifactId>\n\n    <dependencies>\n        <!-- 实现对 ActiveMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-activemq</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-delay/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-delay/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/consumer/Demo02Consumer.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.consumer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.message.Demo02Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jms.annotation.JmsListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo02Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @JmsListener(destination = Demo02Message.QUEUE)\n    public void onMessage(Demo02Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-delay/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/message/Demo02Message.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo02Message implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_02\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo02Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo02Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-delay/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo02Producer.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.producer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.message.Demo02Message;\nimport org.apache.activemq.ScheduledMessage;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jms.core.JmsMessagingTemplate;\nimport org.springframework.stereotype.Component;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@Component\npublic class Demo02Producer {\n\n    @Autowired\n    private JmsMessagingTemplate jmsTemplate;\n\n    public void syncSend(Integer id, Integer delay) {\n        // 创建 Demo02Message 消息\n        Demo02Message message = new Demo02Message();\n        message.setId(id);\n        // 创建 Header\n        Map<String, Object> headers = null;\n        if (delay != null && delay > 0) {\n            headers = new HashMap<>();\n            headers.put(ScheduledMessage.AMQ_SCHEDULED_DELAY, delay);\n        }\n        // 同步发送消息\n        jmsTemplate.convertAndSend(Demo02Message.QUEUE, message, headers);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-delay/src/main/resources/application.yaml",
    "content": "spring:\n  # ActiveMQ 配置项，对应 ActiveMQProperties 配置类\n  activemq:\n    broker-url: tcp://127.0.0.1:61616 # ActiveMQ Broker 的地址\n    user: admin # 账号\n    password: admin # 密码\n    packages:\n      trust-all: true # 可信任的反序列化包\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-delay/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo;\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-delay/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo02ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.producer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo02ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo02Producer producer;\n\n    @Test\n    public void testSyncSend01() throws InterruptedException {\n        // 不设置消息的过期时间\n        this.testSyncSendDelay(null);\n    }\n\n    @Test\n    public void testSyncSend02() throws InterruptedException {\n        // 设置发送消息的过期时间为 5000 毫秒\n        this.testSyncSendDelay(5000);\n    }\n\n    private void testSyncSendDelay(Integer delay) throws InterruptedException {\n        int id = (int) (System.currentTimeMillis() / 1000);\n        producer.syncSend(id, delay);\n        logger.info(\"[testSyncSendDelay][发送编号：[{}] 发送成功]\", id);\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-message-model/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-32-activemq-demo-message-model</artifactId>\n\n    <dependencies>\n        <!-- 实现对 ActiveMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-activemq</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-message-model/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-message-model/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/config/ActiveMQConfig.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.config;\n\nimport org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.jms.config.DefaultJmsListenerContainerFactory;\nimport org.springframework.jms.core.JmsMessagingTemplate;\nimport org.springframework.jms.core.JmsTemplate;\n\nimport javax.jms.ConnectionFactory;\n\n@Configuration\npublic class ActiveMQConfig {\n\n    public static final String CLUSTERING_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME = \"clusteringJmsListenerContainerFactory\";\n    public static final String BROADCAST_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME = \"broadcastJmsListenerContainerFactory\";\n\n    public static final String CLUSTERING_JMS_TEMPLATE_BEAN_NAME = \"clusteringJmsTemplate\";\n    public static final String BROADCAST_JMS_TEMPLATE_BEAN_NAME = \"broadcastJmsTemplate\";\n\n    // ========== 集群消费 =========\n\n    @Bean(CLUSTERING_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME)\n    public DefaultJmsListenerContainerFactory clusteringJmsListenerContainerFactory(\n            DefaultJmsListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {\n        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();\n        configurer.configure(factory, connectionFactory);\n        factory.setPubSubDomain(false);\n        return factory;\n    }\n\n    @Bean(CLUSTERING_JMS_TEMPLATE_BEAN_NAME)\n    public JmsMessagingTemplate clusteringJmsTemplate(ConnectionFactory connectionFactory) {\n        // 创建 JmsTemplate 对象\n        JmsTemplate template = new JmsTemplate(connectionFactory);\n        template.setPubSubDomain(false);\n        // 创建 JmsMessageTemplate\n        return new JmsMessagingTemplate(template);\n    }\n\n    // ========== 广播消费 ==========\n\n    @Bean(BROADCAST_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME)\n    public DefaultJmsListenerContainerFactory broadcastJmsListenerContainerFactory(\n            DefaultJmsListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {\n        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();\n        configurer.configure(factory, connectionFactory);\n        factory.setPubSubDomain(true);\n        return factory;\n    }\n\n    @Bean(BROADCAST_JMS_TEMPLATE_BEAN_NAME)\n    public JmsMessagingTemplate broadcastJmsTemplate(ConnectionFactory connectionFactory) {\n        // 创建 JmsTemplate 对象\n        JmsTemplate template = new JmsTemplate(connectionFactory);\n        template.setPubSubDomain(true);\n        // 创建 JmsMessageTemplate\n        return new JmsMessagingTemplate(template);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-message-model/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/consumer/BroadcastConsumer.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.consumer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.config.ActiveMQConfig;\nimport cn.iocoder.springboot.lab32.activemqdemo.message.BroadcastMessage;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jms.annotation.JmsListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class BroadcastConsumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @JmsListener(destination = BroadcastMessage.TOPIC,\n            containerFactory = ActiveMQConfig.BROADCAST_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME)\n    public void onMessage(BroadcastMessage message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-message-model/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/consumer/ClusteringConsumer.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.consumer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.config.ActiveMQConfig;\nimport cn.iocoder.springboot.lab32.activemqdemo.message.ClusteringMessage;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jms.annotation.JmsListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ClusteringConsumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @JmsListener(destination = ClusteringMessage.QUEUE,\n            containerFactory = ActiveMQConfig.CLUSTERING_JMS_LISTENER_CONTAINER_FACTORY_BEAN_NAME)\n    public void onMessage(ClusteringMessage message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-message-model/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/message/BroadcastMessage.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.message;\n\nimport java.io.Serializable;\n\n/**\n * 广播消费的消息示例\n */\npublic class BroadcastMessage implements Serializable {\n\n    public static final String TOPIC = \"TOPIC_BROADCASTING\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public BroadcastMessage setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"BroadcastMessage{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-message-model/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/message/ClusteringMessage.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.message;\n\nimport java.io.Serializable;\n\n/**\n * 广播消费的消息示例\n */\npublic class ClusteringMessage implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_CLUSTERING\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public ClusteringMessage setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"ClusteringtMessage{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-message-model/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/producer/BroadcastProducer.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.producer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.config.ActiveMQConfig;\nimport cn.iocoder.springboot.lab32.activemqdemo.message.BroadcastMessage;\nimport org.springframework.jms.core.JmsMessagingTemplate;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n@Component\npublic class BroadcastProducer {\n\n    @Resource(name = ActiveMQConfig.BROADCAST_JMS_TEMPLATE_BEAN_NAME)\n    private JmsMessagingTemplate jmsMessagingTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 BroadcastMessage 消息\n        BroadcastMessage message = new BroadcastMessage();\n        message.setId(id);\n        // 同步发送消息\n        jmsMessagingTemplate.convertAndSend(BroadcastMessage.TOPIC, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-message-model/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/producer/ClusteringProducer.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.producer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.config.ActiveMQConfig;\nimport cn.iocoder.springboot.lab32.activemqdemo.message.ClusteringMessage;\nimport org.springframework.jms.core.JmsMessagingTemplate;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\n\n@Component\npublic class ClusteringProducer {\n\n    @Resource(name = ActiveMQConfig.CLUSTERING_JMS_TEMPLATE_BEAN_NAME)\n    private JmsMessagingTemplate jmsTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 ClusteringMessage 消息\n        ClusteringMessage message = new ClusteringMessage();\n        message.setId(id);\n        // 同步发送消息\n        jmsTemplate.convertAndSend(ClusteringMessage.QUEUE, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-message-model/src/main/resources/application.yaml",
    "content": "spring:\n  # ActiveMQ 配置项，对应 ActiveMQProperties 配置类\n  activemq:\n    broker-url: tcp://127.0.0.1:61616 # ActiveMQ Broker 的地址\n    user: admin # 账号\n    password: admin # 密码\n    packages:\n      trust-all: true\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-message-model/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo;\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-message-model/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/producer/BroadcastProducerTest.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.producer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class BroadcastProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private BroadcastProducer producer;\n\n    @Test\n    public void mock() throws InterruptedException {\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        for (int i = 0; i < 3; i++) {\n            int id = (int) (System.currentTimeMillis() / 1000);\n            producer.syncSend(id);\n            logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-message-model/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/producer/ClusteringProducerTest.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.producer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class ClusteringProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private ClusteringProducer producer;\n\n    @Test\n    public void mock() throws InterruptedException {\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        // 发送 3 条消息\n        for (int i = 0; i < 3; i++) {\n            int id = (int) (System.currentTimeMillis() / 1000);\n            producer.syncSend(id);\n            logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-orderly/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-32-activemq-demo-orderly</artifactId>\n\n    <dependencies>\n        <!-- 实现对 ActiveMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-activemq</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-orderly/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-orderly/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/consumer/Demo04Consumer.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.consumer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.message.Demo04Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jms.annotation.JmsListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo04Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @JmsListener(destination = Demo04Message.QUEUE_0)\n    @JmsListener(destination = Demo04Message.QUEUE_1)\n    @JmsListener(destination = Demo04Message.QUEUE_2)\n    @JmsListener(destination = Demo04Message.QUEUE_3)\n    public void onMessage(Demo04Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-orderly/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/message/Demo04Message.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.message;\n\nimport java.io.Serializable;\n\npublic class Demo04Message implements Serializable {\n\n    public static final String QUEUE_BASE = \"QUEUE_DEMO_04-\";\n    public static final String QUEUE_0 = QUEUE_BASE + \"0\";\n    public static final String QUEUE_1 = QUEUE_BASE + \"1\";\n    public static final String QUEUE_2 = QUEUE_BASE + \"2\";\n    public static final String QUEUE_3 = QUEUE_BASE + \"3\";\n\n    public static final int QUEUE_COUNT = 4;\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo04Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo04Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-orderly/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo04Producer.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.producer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.message.Demo04Message;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jms.core.JmsMessagingTemplate;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo04Producer {\n\n    @Autowired\n    private JmsMessagingTemplate jmsTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 Demo04Message 消息\n        Demo04Message message = new Demo04Message();\n        message.setId(id);\n        // 同步发送消息\n        jmsTemplate.convertAndSend(this.getQueue(id), message);\n    }\n\n    private String getQueue(Integer id) {\n        return Demo04Message.QUEUE_BASE + (id % Demo04Message.QUEUE_COUNT);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-orderly/src/main/resources/application.yaml",
    "content": "spring:\n  # ActiveMQ 配置项，对应 ActiveMQProperties 配置类\n  activemq:\n    broker-url: tcp://127.0.0.1:61616 # ActiveMQ Broker 的地址\n    user: admin # 账号\n    password: admin # 密码\n    packages:\n      trust-all: true # 可信任的反序列化包\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-orderly/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo;\n"
  },
  {
    "path": "lab-32/lab-32-activemq-demo-orderly/src/test/java/cn/iocoder/springboot/lab32/activemqdemo/producer/Demo04ProducerTest.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo.producer;\n\nimport cn.iocoder.springboot.lab32.activemqdemo.Application;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.concurrent.CountDownLatch;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class Demo04ProducerTest {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private Demo04Producer producer;\n\n    @Test\n    public void testSyncSend() throws InterruptedException {\n        for (int i = 0; i < 2; i++) {\n            for (int id = 0; id < 4; id++) {\n                producer.syncSend(id);\n//                logger.info(\"[testSyncSend][发送编号：[{}] 发送成功]\", id);\n            }\n        }\n\n        // 阻塞等待，保证消费\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-native/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-32</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-32-activemq-native</artifactId>\n\n    <dependencies>\n        <!-- 引入 ActiveMQ 客户端依赖 -->\n        <dependency>\n            <groupId>org.apache.activemq</groupId>\n            <artifactId>activemq-client</artifactId>\n            <version>5.15.10</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-32/lab-32-activemq-native/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/ActiveMQConsumer.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo;\n\nimport javax.jms.*; // 使用 JMS API\n\nimport java.util.concurrent.TimeUnit;\n\npublic class ActiveMQConsumer {\n\n    public static void main(String[] args) throws JMSException {\n        // 创建连接\n        Connection connection = ActiveMQProducer.getConnection();\n\n        // 创建会话\n        final Session session = ActiveMQProducer.getSession(connection);\n\n        // 创建队列\n        Queue queue = ActiveMQProducer.getQueue(session);\n\n        // 创建 Consumer\n        MessageConsumer consumer = session.createConsumer(queue);\n        consumer.setMessageListener(new MessageListener() {\n\n            public void onMessage(Message message) {\n                TextMessage textMessage = (TextMessage) message;\n                try {\n                    System.out.println(String.format(\"[线程：%s][消息编号：%s][消息内容：%s]\",\n                            Thread.currentThread(), textMessage.getJMSMessageID(), textMessage.getText()));\n                } catch (JMSException e) {\n                    throw new RuntimeException(e);\n                }\n            }\n\n        });\n\n        // 关闭\n        try {\n            TimeUnit.HOURS.sleep(1);\n        } catch (InterruptedException ignore) {\n        }\n        session.close();\n        connection.close();\n    }\n\n}\n"
  },
  {
    "path": "lab-32/lab-32-activemq-native/src/main/java/cn/iocoder/springboot/lab32/activemqdemo/ActiveMQProducer.java",
    "content": "package cn.iocoder.springboot.lab32.activemqdemo;\n\nimport org.apache.activemq.ActiveMQConnectionFactory; // 使用 ActiveMQ 的客户端实现\n\nimport javax.jms.*; // 使用 JMS API\n\npublic class ActiveMQProducer {\n\n    private static final String BROKER_URL = \"tcp://127.0.0.1:61616\";\n    private static final String USERNAME = \"admin\";\n    private static final String PASSWORD = \"admin\";\n\n    private static final String QUEUE_NAME = \"queue_demo\"; // 只有 QUEUE_NAME 需要共享给 ActiveMQConsumer\n\n    public static void main(String[] args) throws JMSException {\n        // 创建连接\n        Connection connection = getConnection();\n\n        // 创建会话\n        Session session = getSession(connection);\n\n        // 创建队列\n        Queue queue = getQueue(session);\n\n        // 创建 Producer\n        MessageProducer producer = session.createProducer(queue);\n\n        // 发送 3 条消息\n        for (int i = 0; i < 3; i++) {\n            Message message = session.createTextMessage(\"Hello World\" + i);\n            producer.send(message);\n        }\n\n        // 关闭\n        session.close();\n        connection.close();\n    }\n\n    public static Connection getConnection() throws JMSException {\n        // 创建连接\n        ConnectionFactory factory = new ActiveMQConnectionFactory(USERNAME, PASSWORD, BROKER_URL);\n        Connection connection = factory.createConnection();\n        // 启动连接\n        connection.start();\n        return connection;\n    }\n\n    public static Session getSession(Connection connection) throws JMSException {\n        // 第一个方法参数 transacted ，是否开启事务。这里设置为 false ，无需开启\n        // 第二个方法参数 acknowledgeMode ，确认模式。这里设置为 AUTO_ACKNOWLEDGE ，自动确认。推荐阅读 https://my.oschina.net/thinwonton/blog/995291\n        return connection.createSession(false, Session.AUTO_ACKNOWLEDGE);\n    }\n\n    public static Queue getQueue(Session session) throws JMSException {\n        return session.createQueue(QUEUE_NAME);\n    }\n\n}\n"
  },
  {
    "path": "lab-32/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-32</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-32-activemq-native</module>\n        <module>lab-32-activemq-demo</module>\n        <module>lab-32-activemq-demo-message-model</module>\n        <module>lab-32-activemq-demo-delay</module>\n        <module>lab-32-activemq-demo-concurrency</module>\n        <module>lab-32-activemq-demo-orderly</module>\n        <module>lab-32-activemq-demo-consume-retry</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-32/《芋道 Spring Boot 消息队列 ActiveMQ 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/ActiveMQ/?github>\n"
  },
  {
    "path": "lab-33/lab-33-shiro-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.10.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-33-shiro-demo</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Shiro 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.shiro</groupId>\n            <artifactId>shiro-spring-boot-starter</artifactId>\n            <version>1.4.2</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-33/lab-33-shiro-demo/src/main/java/cn/iocoder/springboot/lab01/shirodemo/Application.java",
    "content": "package cn.iocoder.springboot.lab01.shirodemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-33/lab-33-shiro-demo/src/main/java/cn/iocoder/springboot/lab01/shirodemo/config/ShiroConfig.java",
    "content": "package cn.iocoder.springboot.lab01.shirodemo.config;\n\nimport org.apache.shiro.realm.Realm;\nimport org.apache.shiro.realm.SimpleAccountRealm;\nimport org.apache.shiro.spring.web.ShiroFilterFactoryBean;\nimport org.apache.shiro.web.mgt.DefaultWebSecurityManager;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\n@Configuration\npublic class ShiroConfig {\n\n    @Bean\n    public Realm realm() {\n        // 创建 SimpleAccountRealm 对象\n        SimpleAccountRealm realm = new SimpleAccountRealm();\n        // 添加两个用户。参数分别是 username、password、roles 。\n        realm.addAccount(\"admin\", \"admin\", \"ADMIN\");\n        realm.addAccount(\"normal\", \"normal\", \"NORMAL\");\n        return realm;\n    }\n\n    @Bean\n    public DefaultWebSecurityManager securityManager() {\n        // 创建 DefaultWebSecurityManager 对象\n        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();\n        // 设置其使用的 Realm\n        securityManager.setRealm(this.realm());\n        return securityManager;\n    }\n\n    @Bean\n    public ShiroFilterFactoryBean shiroFilterFactoryBean() {\n        // 创建 ShiroFilterFactoryBean 对象，用于创建 ShiroFilter 过滤器\n        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();\n\n        // 设置 SecurityManager\n        filterFactoryBean.setSecurityManager(this.securityManager());\n\n        // 设置 URL 们\n        filterFactoryBean.setLoginUrl(\"/login\"); // 登陆 URL\n        filterFactoryBean.setSuccessUrl(\"/login_success\"); // 登陆成功 URL\n        filterFactoryBean.setUnauthorizedUrl(\"/unauthorized\"); // 无权限 URL\n\n        // 设置 URL 的权限配置\n        filterFactoryBean.setFilterChainDefinitionMap(this.filterChainDefinitionMap());\n\n        return filterFactoryBean;\n    }\n\n    private Map<String, String> filterChainDefinitionMap() {\n        Map<String, String> filterMap = new LinkedHashMap<>(); // 注意要使用有序的 LinkedHashMap ，顺序匹配\n        filterMap.put(\"/test/echo\", \"anon\"); // 允许匿名访问\n        filterMap.put(\"/test/admin\", \"roles[ADMIN]\"); // 需要 ADMIN 角色\n        filterMap.put(\"/test/normal\", \"roles[NORMAL]\"); // 需要 NORMAL 角色\n        filterMap.put(\"/logout\", \"logout\"); // 退出\n        filterMap.put(\"/**\", \"authc\"); // 默认剩余的 URL ，需要经过认证\n        return filterMap;\n    }\n\n}\n"
  },
  {
    "path": "lab-33/lab-33-shiro-demo/src/main/java/cn/iocoder/springboot/lab01/shirodemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab01.shirodemo.controller;\n\nimport org.apache.shiro.authz.annotation.RequiresGuest;\nimport org.apache.shiro.authz.annotation.RequiresRoles;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @RequiresGuest\n    @GetMapping(\"/echo\")\n    public String demo() {\n        return \"示例返回\";\n    }\n\n    @GetMapping(\"/home\")\n    public String home() {\n        return \"我是首页\";\n    }\n\n    @RequiresRoles(\"ADMIN\")\n    @GetMapping(\"/admin\")\n    public String admin() {\n        return \"我是管理员\";\n    }\n\n    @RequiresRoles(\"NORMAL\")\n    @GetMapping(\"/normal\")\n    public String normal() {\n        return \"我是普通用户\";\n    }\n\n}\n"
  },
  {
    "path": "lab-33/lab-33-shiro-demo/src/main/java/cn/iocoder/springboot/lab01/shirodemo/controller/SecurityController.java",
    "content": "package cn.iocoder.springboot.lab01.shirodemo.controller;\n\nimport org.apache.shiro.SecurityUtils;\nimport org.apache.shiro.authc.ExpiredCredentialsException;\nimport org.apache.shiro.authc.IncorrectCredentialsException;\nimport org.apache.shiro.authc.LockedAccountException;\nimport org.apache.shiro.authc.UnknownAccountException;\nimport org.apache.shiro.subject.Subject;\nimport org.apache.shiro.web.filter.authc.FormAuthenticationFilter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport javax.servlet.http.HttpServletRequest;\n\n@Controller\n@RequestMapping(\"/\")\npublic class SecurityController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/login\")\n    public String loginPage() {\n        return \"login.html\";\n    }\n\n    @ResponseBody\n    @PostMapping(\"/login\")\n    public String login(HttpServletRequest request) {\n        // 判断是否已经登陆\n        Subject subject = SecurityUtils.getSubject();\n        if (subject.getPrincipal() != null) {\n            return \"你已经登陆账号：\" + subject.getPrincipal();\n        }\n\n        // 获得登陆失败的原因\n        String shiroLoginFailure = (String) request.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);\n        // 翻译成人类看的懂的提示\n        String msg = \"\";\n        if (UnknownAccountException.class.getName().equals(shiroLoginFailure)) {\n            msg = \"账号不存在\";\n        } else if (IncorrectCredentialsException.class.getName().equals(shiroLoginFailure)) {\n            msg = \"密码不正确\";\n        } else if (LockedAccountException.class.getName().equals(shiroLoginFailure)) {\n            msg = \"账号被锁定\";\n        } else if (ExpiredCredentialsException.class.getName().equals(shiroLoginFailure)) {\n            msg = \"账号已过期\";\n        } else {\n            msg = \"未知\";\n            logger.error(\"[login][未知登陆错误：{}]\", shiroLoginFailure);\n        }\n        return \"登陆失败，原因：\" + msg;\n    }\n\n    @ResponseBody\n    @GetMapping(\"/login_success\")\n    public String loginSuccess() {\n        return \"登陆成功\";\n    }\n\n    @ResponseBody\n    @GetMapping(\"/unauthorized\")\n    public String unauthorized() {\n        return \"你没有权限\";\n    }\n\n}\n"
  },
  {
    "path": "lab-33/lab-33-shiro-demo/src/main/java/cn/iocoder/springboot/lab01/shirodemo/controller/TestController.java",
    "content": "package cn.iocoder.springboot.lab01.shirodemo.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/test\")\npublic class TestController {\n\n    @GetMapping(\"/demo\")\n    public String demo() {\n        return \"示例返回\";\n    }\n\n    @GetMapping(\"/home\")\n    public String home() {\n        return \"我是首页\";\n    }\n\n    @GetMapping(\"/admin\")\n    public String admin() {\n        return \"我是管理员\";\n    }\n\n    @GetMapping(\"/normal\")\n    public String normal() {\n        return \"我是普通用户\";\n    }\n\n}\n"
  },
  {
    "path": "lab-33/lab-33-shiro-demo/src/main/resources/static/login.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <title>登陆页面</title>\n</head>\n<body>\n    <form action=\"/login\" method=\"post\">\n        用户名：<input type=\"text\" name=\"username\"/> <br />\n        密码：<input type=\"password\" name=\"password\"/> <br />\n        <input type=\"submit\" value=\"登录\"/>\n    </form>\n</body>\n</html>\n"
  },
  {
    "path": "lab-33/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-33</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-33-shiro-demo</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-33/《芋道 Spring Boot 安全框架 Shiro 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Shiro/?github>\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-34-actuator-demo</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/Application.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo/src/main/resources/application.yaml",
    "content": "management:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n        exclude: # 在 include 的基础上，需要排除的端点。通过设置 * ，可以排除所有端点。\n#  server:\n#    port: 8081\n#  endpoint:\n#    shutdown:\n#      enabled: true\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-auditevents/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-34-actuator-demo-auditevents</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n            <version>2.2.0.RELEASE</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-auditevents/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/Application.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-auditevents/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/config/ActuateConfig.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo.config;\n\nimport org.springframework.boot.actuate.audit.AuditEventRepository;\nimport org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class ActuateConfig {\n\n    @Bean\n    public AuditEventRepository auditEventRepository() {\n        return new InMemoryAuditEventRepository();\n    }\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-auditevents/src/main/resources/application.yaml",
    "content": "management:\n  endpoint:\n    # AuditEventsEndpoint 端点配置项\n    auditevents:\n      enabled: true # 是否开启。默认为 true 开启\n\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n        exclude: # 在 include 的基础上，需要排除的端点。通过设置 * ，可以排除所有端点。\n\nspring:\n  # Spring Security 配置项，对应 SecurityProperties 配置类\n  security:\n    # 配置默认的 InMemoryUserDetailsManager 的用户账号与密码。\n    user:\n      name: user # 账号\n      password: user # 密码\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-custom-endpoint/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-34-actuator-demo-custom-endpoint</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-custom-endpoint/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/Application.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-custom-endpoint/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/endpoint/DemoEndPoint.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo.endpoint;\n\nimport org.springframework.boot.actuate.endpoint.annotation.Endpoint;\nimport org.springframework.boot.actuate.endpoint.annotation.ReadOperation;\nimport org.springframework.stereotype.Component;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@Component\n@Endpoint(id = \"demo\")\npublic class DemoEndPoint {\n\n    @ReadOperation\n    public Map<String, String> hello() {\n        Map<String, String> result = new HashMap<>();\n        result.put(\"作者\", \"yudaoyuanma\");\n        result.put(\"秃头\", \"true\");\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-custom-endpoint/src/main/resources/application.yaml",
    "content": "management:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n        exclude: # 在 include 的基础上，需要排除的端点。通过设置 * ，可以排除所有端点。\n#  endpoint:\n#    shutdown:\n#      enabled: true\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-health/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-34-actuator-demo-health</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-health/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/Application.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-health/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/actuate/DemoHealthIndicator.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo.actuate;\n\nimport org.springframework.boot.actuate.health.AbstractHealthIndicator;\nimport org.springframework.boot.actuate.health.Health;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class DemoHealthIndicator extends AbstractHealthIndicator {\n\n    @Override\n    protected void doHealthCheck(Health.Builder builder) {\n        // 判断是否健康\n        boolean success = checkSuccess();\n\n        // 如果健康，则标记状态为 UP\n        if (success) {\n            builder.up().build();\n            return;\n        }\n\n        // 如果不健康，则标记状态为 DOWN\n        builder.down().withDetail(\"msg\", \"我就是做个示例而已\");\n    }\n\n    private boolean checkSuccess() {\n        return false;\n    }\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-health/src/main/resources/application.yaml",
    "content": "management:\n  endpoint:\n    # Health 端点配置项，对应 HealthProperties 配置类\n    health:\n      enabled: true # 是否开启。默认为 true 开启。\n      show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户；可选 ALWAYS 总是展示。\n      status:\n        http-mapping: # 设置不同健康状态对应的响应状态码\n          DOWN: 503\n        order: DOWN, OUT_OF_SERVICE, UP, UNKNOWN # 状态排序。\n  health:\n    # DiskSpaceHealthIndicator 配置项，对应 DiskSpaceHealthIndicatorProperties\n    diskspace:\n      enabled: true # 是否开启。默认为 true 开启。\n      path: . # 目录。默认为 . 当前目录。\n      threshold: # 剩余空间的阀值。默认为 10M 。\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n        exclude: # 在 include 的基础上，需要排除的端点。通过设置 * ，可以排除所有端点。\n\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-httptrace/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-34-actuator-demo-httptrace</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-httptrace/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/Application.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-httptrace/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/config/ActuateConfig.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo.config;\n\nimport org.springframework.boot.actuate.trace.http.HttpTraceRepository;\nimport org.springframework.boot.actuate.trace.http.InMemoryHttpTraceRepository;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class ActuateConfig {\n\n    @Bean\n    public HttpTraceRepository httpTraceRepository() {\n        return new InMemoryHttpTraceRepository();\n    }\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-httptrace/src/main/resources/application.yaml",
    "content": "management:\n  endpoint:\n    # HttpTrace 端点配置项\n    httptrace:\n      enabled: true # 是否开启。默认为 true 开启\n  # HttpTrace 的具体配置项，对应 HttpTraceProperties 配置类\n  trace:\n    http:\n      enabled: true # 是否开启。默认为 true 开启。\n      include: # 包含的 trace 项的数组。默认不包含 COOKIE_HEADERS、AUTHORIZATION_HEADER 项。\n\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n        exclude: # 在 include 的基础上，需要排除的端点。通过设置 * ，可以排除所有端点。\n\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-info/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-34-actuator-demo-info</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>build-info</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n\n            <plugin>\n                <groupId>pl.project13.maven</groupId>\n                <artifactId>git-commit-id-plugin</artifactId>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>revision</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-info/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/Application.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-info/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/config/ActuateConfig.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo.config;\n\nimport org.springframework.boot.actuate.info.InfoContributor;\nimport org.springframework.boot.actuate.info.MapInfoContributor;\nimport org.springframework.boot.actuate.info.SimpleInfoContributor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.util.Collections;\n\n@Configuration\npublic class ActuateConfig {\n\n    @Bean\n    public InfoContributor exampleInfo() {\n        return new SimpleInfoContributor(\"example\",\n                Collections.singletonMap(\"key\", \"value\"));\n    }\n\n    @Bean\n    public InfoContributor exampleInfo02() {\n        return new MapInfoContributor(Collections.singletonMap(\"example02\", \"nicai\"));\n    }\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-info/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/demo/DemoInfoContributor.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo.demo;\n\nimport org.springframework.boot.actuate.info.Info;\nimport org.springframework.boot.actuate.info.InfoContributor;\nimport org.springframework.stereotype.Component;\n\nimport java.util.Collections;\n\n@Component\npublic class DemoInfoContributor implements InfoContributor {\n\n    @Override\n    public void contribute(Info.Builder builder) {\n        builder.withDetail(\"demo\",\n                Collections.singletonMap(\"key\", \"value\"));\n    }\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-info/src/main/resources/application.yaml",
    "content": "management:\n  endpoint:\n    # Info 端点配置项\n    info:\n      enabled: true # 是否开启。默认为 true 开启。\n  info:\n    # EnvironmentInfoContributor 的配置项\n    env:\n      enabled: true\n    # BuildInfoContributor 的配置属性\n    build:\n      enabled: true\n    # GitInfoContributor 的配置属性\n    git:\n      enabled: true\n      mode: SIMPLE # Git 信息展示模式。SIMPLE 默认，只展示精简的 Git 版本信息；FULL 模式，展示完整的 Git 版本信息。\n\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n        exclude: # 在 include 的基础上，需要排除的端点。通过设置 * ，可以排除所有端点。\n\n# info 配置项\ninfo:\n  app:\n    java:\n      source: @java.version@\n      target: @java.version@\n    encoding: UTF-8\n    version: @project.version@\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-metrics/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-34-actuator-demo-metrics</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-metrics/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/Application.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-metrics/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo.controller;\n\nimport io.micrometer.core.instrument.Counter;\nimport io.micrometer.core.instrument.Metrics;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    /**\n     * DEMO 访问次数 Metrics\n     */\n    private static final Counter METRICS_DEMO_COUNT = Counter\n            .builder(\"demo.visit.count\") // 指标的名字\n            .description(\"demo 访问次数\") // 指标的描述\n            .baseUnit(\"次\") // 指标的单位\n            .tag(\"test\", \"nicai\") // 自定义标签\n            .register(Metrics.globalRegistry); // 注册到全局 MeterRegistry 指标注册表\n//    private static final Counter METRICS_DEMO_COUNT = Metrics.counter(\"demo.visit.count\");\n\n    @GetMapping(\"/visit\")\n    public String visit() {\n        // 增加次数\n        METRICS_DEMO_COUNT.increment();\n        return \"Demo 示例\";\n    }\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-metrics/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/controller/ExampleController.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo.controller;\n\nimport io.micrometer.core.annotation.Timed;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n// 参考 https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-metrics-spring-mvc 文档\n// 需要设置 management.metrics.web.server.request.autotime.enabled = false\n@RestController\n@RequestMapping(\"/example\")\n@Timed\npublic class ExampleController {\n\n    @GetMapping(\"/visit\")\n//    @Counted(value = \"example.visit.count\", description = \"example 访问次数\"\n    @Timed(value = \"all.people\", longTask = true)\n    public String visit() {\n        return \"Example 示例\";\n    }\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-metrics/src/main/resources/application.yaml",
    "content": "#spring:\n#  application:\n#    name: demo-application\n\nmanagement:\n  endpoint:\n    # Metrics 端点配置项\n    metrics:\n      enabled: true # 是否开启。默认为 true 开启。\n  # Metrics 的具体配置项，对应 MetricsProperties 配置类\n  metrics:\n    # 设置指定前缀的指标是否开启\n    enable:\n      xxx: false\n    # 通用 tag\n    tags:\n      application: demo-application\n\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n        exclude: # 在 include 的基础上，需要排除的端点。通过设置 * ，可以排除所有端点。\n\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-security/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-34-actuator-demo-security</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-security/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/Application.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-security/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/config/SecurityConfig.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo.config;\n\nimport org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\n\n@Configuration\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n        // 访问 EndPoint 地址，需要经过认证，并且拥有 ADMIN 角色\n        http.requestMatcher(EndpointRequest.toAnyEndpoint()).authorizeRequests((requests) ->\n                requests.anyRequest().hasRole(\"ADMIN\"));\n        // 开启 Basic Auth\n        http.httpBasic();\n    }\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-demo-security/src/main/resources/application.yaml",
    "content": "management:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n        exclude: # 在 include 的基础上，需要排除的端点。通过设置 * ，可以排除所有端点。\n#  server:\n#    port: 8081\n#  endpoint:\n#    shutdown:\n#      enabled: true\n\nspring:\n  # Spring Security 配置项，对应 SecurityProperties 配置类\n  security:\n    # 配置默认的 InMemoryUserDetailsManager 的用户账号与密码。\n    user:\n      name: user # 账号\n      password: user # 密码\n      roles: ADMIN # 拥有角色\n"
  },
  {
    "path": "lab-34/lab-34-actuator-test/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-34-actuator-test</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-34/lab-34-actuator-test/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/Application.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-test/src/main/java/cn/iocoder/springboot/lab34/actuatordemo/controller/TestController.java",
    "content": "package cn.iocoder.springboot.lab34.actuatordemo.controller;\n\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class TestController {\n\n\n\n}\n"
  },
  {
    "path": "lab-34/lab-34-actuator-test/src/main/resources/application.yaml",
    "content": "management:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n        exclude: # 在 include 的基础上，需要排除的端点。通过设置 * ，可以排除所有端点。\n#  server:\n#    port: 8081\n#  endpoint:\n#    shutdown:\n#      enabled: true\n"
  },
  {
    "path": "lab-34/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-34</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-34-actuator-demo</module>\n        <module>lab-34-actuator-demo-health</module>\n        <module>lab-34-actuator-demo-info</module>\n        <module>lab-34-actuator-demo-metrics</module>\n        <module>lab-34-actuator-demo-httptrace</module>\n        <module>lab-34-actuator-demo-auditevents</module>\n        <module>lab-34-actuator-demo-custom-endpoint</module>\n        <module>lab-34-actuator-demo-security</module>\n\n        <module>lab-34-actuator-test</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-34/《芋道 Spring Boot 监控端点 Actuator 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Actuator/?github>\n"
  },
  {
    "path": "lab-35/lab-35-admin-01-adminserver/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-35-admin-01-adminserver</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring Boot Admin Server 的自动化配置 -->\n        <!--\n            包含 1. spring-boot-admin-server ：Server 端\n                2. spring-boot-admin-server-ui ：UI 界面\n                3. spring-boot-admin-server-cloud ：对 Spring Cloud 的接入\n        -->\n        <dependency>\n            <groupId>de.codecentric</groupId>\n            <artifactId>spring-boot-admin-starter-server</artifactId>\n            <version>2.2.0</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-35/lab-35-admin-01-adminserver/src/main/java/cn/iocoder/springboot/lab35/adminserver/AdminServerApplication.java",
    "content": "package cn.iocoder.springboot.lab35.adminserver;\n\nimport de.codecentric.boot.admin.server.config.EnableAdminServer;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@EnableAdminServer\npublic class AdminServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AdminServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-35/lab-35-admin-01-demo-application/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-35-admin-01-demo-application</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Boot Admin Client 的自动化配置 -->\n        <dependency>\n            <groupId>de.codecentric</groupId>\n            <artifactId>spring-boot-admin-starter-client</artifactId>\n            <version>2.2.0</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-35/lab-35-admin-01-demo-application/src/main/java/cn/iocoder/springboot/lab35/demo/DemoApplication.java",
    "content": "package cn.iocoder.springboot.lab35.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-35/lab-35-admin-01-demo-application/src/main/resources/application.yaml",
    "content": "management:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\nspring:\n  application:\n    name: demo-application # 应用名\n  boot:\n    admin:\n      client:\n        url: http://127.0.0.1:8080 # Spring Boot Admin Server 地址\n\nserver:\n  port: 18080 # 设置自定义 Server 端口，避免和 Spring Boot Admin Server 端口冲突。\n"
  },
  {
    "path": "lab-35/lab-35-admin-02-adminserver/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-35-admin-02-adminserver</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring Boot Admin Server 的自动化配置 -->\n        <!--\n            包含 1. spring-boot-admin-server ：Server 端\n                2. spring-boot-admin-server-ui ：UI 界面\n                3. spring-boot-admin-server-cloud ：对 Spring Cloud 的接入\n        -->\n        <dependency>\n            <groupId>de.codecentric</groupId>\n            <artifactId>spring-boot-admin-starter-server</artifactId>\n            <version>2.2.0</version>\n        </dependency>\n\n        <!-- 实现对 Eureka Client 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>\n            <version>2.2.1.RELEASE</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-35/lab-35-admin-02-adminserver/src/main/java/cn/iocoder/springboot/lab35/adminserver/AdminServerApplication.java",
    "content": "package cn.iocoder.springboot.lab35.adminserver;\n\nimport de.codecentric.boot.admin.server.config.EnableAdminServer;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@SpringBootApplication\n@EnableAdminServer\n@EnableDiscoveryClient\npublic class AdminServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AdminServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-35/lab-35-admin-02-adminserver/src/main/resources/application.yaml",
    "content": "eureka:\n  client:\n    service-url:\n      defaultZone: http://127.0.0.1:8761/eureka\n    register-with-eureka: false # 不注册到 Eureka 中\n"
  },
  {
    "path": "lab-35/lab-35-admin-02-demo-application/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-35-admin-02-demo-application</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n\n        <!-- 实现对 Eureka Client 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>\n            <version>2.2.1.RELEASE</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-35/lab-35-admin-02-demo-application/src/main/java/cn/iocoder/springboot/lab35/demo/Demo01Application.java",
    "content": "package cn.iocoder.springboot.lab35.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class Demo01Application {\n\n    public static void main(String[] args) {\n        System.setProperty(\"server.port\", \"18081\"); // 端口 18081\n        SpringApplication.run(Demo01Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-35/lab-35-admin-02-demo-application/src/main/java/cn/iocoder/springboot/lab35/demo/Demo02Application.java",
    "content": "package cn.iocoder.springboot.lab35.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\n\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class Demo02Application {\n\n    public static void main(String[] args) {\n        System.setProperty(\"server.port\", \"18082\");  // 端口 18082\n        SpringApplication.run(Demo02Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-35/lab-35-admin-02-demo-application/src/main/resources/application.yaml",
    "content": "management:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\nspring:\n  application:\n    name: demo-application # 应用名\n\neureka:\n  client:\n    service-url:\n      defaultZone: http://127.0.0.1:8761/eureka\n"
  },
  {
    "path": "lab-35/lab-35-admin-02-eurekaserver/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-35-admin-02-eurekaserver</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Eureka Server 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>\n            <version>2.2.1.RELEASE</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-35/lab-35-admin-02-eurekaserver/src/main/java/cn/iocoder/springboot/lab35/eurekaserver/EurekaServerApplication.java",
    "content": "package cn.iocoder.springboot.lab35.eurekaserver;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n@SpringBootApplication\n@EnableEurekaServer\npublic class EurekaServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(EurekaServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-35/lab-35-admin-02-eurekaserver/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: eureka-server # 应用名\n\nserver:\n  port: 8761 # 自定义服务器端口，避免冲突\n\neureka:\n  client:\n    register-with-eureka: false # 不注册到 Eureka 中\n    fetch-registry: false # 不从 Eureka 拉取注册信息\n"
  },
  {
    "path": "lab-35/lab-35-admin-03-adminserver/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-35-admin-03-adminserver</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring Boot Admin Server 的自动化配置 -->\n        <!--\n            包含 1. spring-boot-admin-server ：Server 端\n                2. spring-boot-admin-server-ui ：UI 界面\n                3. spring-boot-admin-server-cloud ：对 Spring Cloud 的接入\n        -->\n        <dependency>\n            <groupId>de.codecentric</groupId>\n            <artifactId>spring-boot-admin-starter-server</artifactId>\n            <version>2.2.0</version>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-35/lab-35-admin-03-adminserver/src/main/java/cn/iocoder/springboot/lab35/adminserver/AdminServerApplication.java",
    "content": "package cn.iocoder.springboot.lab35.adminserver;\n\nimport de.codecentric.boot.admin.server.config.EnableAdminServer;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@EnableAdminServer\npublic class AdminServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AdminServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-35/lab-35-admin-03-adminserver/src/main/java/cn/iocoder/springboot/lab35/adminserver/config/SecurityConfig.java",
    "content": "package cn.iocoder.springboot.lab35.adminserver.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;\nimport org.springframework.security.config.web.server.ServerHttpSecurity;\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\n\n@Configuration\n@EnableWebFluxSecurity // 开启 Security 对 WebFlux 的安全功能\npublic class SecurityConfig {\n\n    @Bean\n    public MapReactiveUserDetailsService userDetailsService() {\n        // 创建一个用户\n        UserDetails user = User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"user\")\n                .roles(\"USER\")\n                .build();\n\n        // 如果胖友有更多用户的诉求，这里可以继续创建\n\n        // 创建 MapReactiveUserDetailsService\n        return new MapReactiveUserDetailsService(user);\n    }\n\n    @Bean\n    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n        http.authorizeExchange(exchanges -> // 设置权限配置\n                exchanges\n                        .pathMatchers(\"/assets/**\").permitAll() // 静态资源，允许匿名访问\n                        .pathMatchers(\"/login\").permitAll() // 登陆接口，允许匿名访问\n                        .anyExchange().authenticated() //\n        )\n        .formLogin().loginPage(\"/login\") // 登陆页面\n        .and().logout().logoutUrl(\"/logout\") // 登出界面\n        .and().httpBasic() // HTTP Basic 认证方式\n        .and().csrf().disable(); // csrf 禁用\n        return http.build();\n    }\n\n}\n"
  },
  {
    "path": "lab-35/lab-35-admin-03-demo-application/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-35-admin-03-demo-application</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Boot Admin Client 的自动化配置 -->\n        <dependency>\n            <groupId>de.codecentric</groupId>\n            <artifactId>spring-boot-admin-starter-client</artifactId>\n            <version>2.2.0</version>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-35/lab-35-admin-03-demo-application/src/main/java/cn/iocoder/springboot/lab35/demo/DemoApplication.java",
    "content": "package cn.iocoder.springboot.lab35.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-35/lab-35-admin-03-demo-application/src/main/resources/application.yaml",
    "content": "management:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\nspring:\n  application:\n    name: demo-application # 应用名\n\n  # Spring Security 配置项，对应 SecurityProperties 配置类\n  security:\n    # 配置默认的 InMemoryUserDetailsManager 的用户账号与密码。\n    user:\n      name: test # 账号\n      password: test # 密码\n\n  boot:\n    admin:\n      client:\n        url: http://127.0.0.1:8080 # Spring Boot Admin Server 地址\n        username: user # Spring Boot Admin Server 的认证账号\n        password: user # Spring Boot Admin Server 的认证密码\n        instance:\n          metadata:\n            user.name: ${spring.security.user.name} # Actuator 端点的认证账号\n            user.password: ${spring.security.user.password} # Actuator 端点的认证密码\n\nserver:\n  port: 18080 # 设置自定义 Server 端口，避免和 Spring Boot Admin Server 端口冲突。\n"
  },
  {
    "path": "lab-35/lab-35-admin-04-adminserver/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-35-admin-04-adminserver</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring Boot Admin Server 的自动化配置 -->\n        <!--\n            包含 1. spring-boot-admin-server ：Server 端\n                2. spring-boot-admin-server-ui ：UI 界面\n                3. spring-boot-admin-server-cloud ：对 Spring Cloud 的接入\n        -->\n        <dependency>\n            <groupId>de.codecentric</groupId>\n            <artifactId>spring-boot-admin-starter-server</artifactId>\n            <version>2.2.0</version>\n        </dependency>\n\n        <!-- 实现对 Java Mail 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-mail</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-35/lab-35-admin-04-adminserver/src/main/java/cn/iocoder/springboot/lab35/adminserver/AdminServerApplication.java",
    "content": "package cn.iocoder.springboot.lab35.adminserver;\n\nimport de.codecentric.boot.admin.server.config.EnableAdminServer;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@EnableAdminServer\npublic class AdminServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AdminServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-35/lab-35-admin-04-adminserver/src/main/resources/application.yaml",
    "content": "spring:\n  mail: # 配置发送告警的邮箱\n    host: smtp.126.com\n    username: wwbmlhh@126.com\n    password: '******'\n    default-encoding: UTF-8\n\n  boot:\n    admin:\n      notify:\n        mail:\n          from: ${spring.mail.username} # 告警发件人\n          to: 7685413@qq.com # 告警收件人\n"
  },
  {
    "path": "lab-35/lab-35-admin-05-adminserver/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-35-admin-05-adminserver</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring Boot Admin Server 的自动化配置 -->\n        <!--\n            包含 1. spring-boot-admin-server ：Server 端\n                2. spring-boot-admin-server-ui ：UI 界面\n                3. spring-boot-admin-server-cloud ：对 Spring Cloud 的接入\n        -->\n        <dependency>\n            <groupId>de.codecentric</groupId>\n            <artifactId>spring-boot-admin-starter-server</artifactId>\n            <version>2.2.0</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-35/lab-35-admin-05-adminserver/src/main/java/cn/iocoder/springboot/lab35/adminserver/AdminServerApplication.java",
    "content": "package cn.iocoder.springboot.lab35.adminserver;\n\nimport de.codecentric.boot.admin.server.config.EnableAdminServer;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@EnableAdminServer\npublic class AdminServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AdminServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-35/lab-35-admin-05-adminserver/src/main/java/cn/iocoder/springboot/lab35/adminserver/notify/LoggerNotifier.java",
    "content": "package cn.iocoder.springboot.lab35.adminserver.notify;\n\nimport de.codecentric.boot.admin.server.domain.entities.Instance;\nimport de.codecentric.boot.admin.server.domain.entities.InstanceRepository;\nimport de.codecentric.boot.admin.server.domain.events.InstanceEvent;\nimport de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;\nimport de.codecentric.boot.admin.server.notify.AbstractEventNotifier;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\nimport reactor.core.publisher.Mono;\n\n@Component\npublic class LoggerNotifier extends AbstractEventNotifier {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    public LoggerNotifier(InstanceRepository repository) {\n        super(repository);\n    }\n\n    @Override\n    protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {\n        return Mono.fromRunnable(() -> {\n            if (event instanceof InstanceStatusChangedEvent) {\n                logger.info(\"Instance {} ({}) is {}\", instance.getRegistration().getName(), event.getInstance(),\n                        ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus());\n            } else {\n                logger.info(\"Instance {} ({}) {}\", instance.getRegistration().getName(), event.getInstance(), event.getType());\n            }\n        });\n    }\n\n}\n"
  },
  {
    "path": "lab-35/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-35</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-35-admin-01-adminserver</module>\n        <module>lab-35-admin-01-demo-application</module>\n\n        <module>lab-35-admin-02-adminserver</module>\n        <module>lab-35-admin-02-demo-application</module>\n        <module>lab-35-admin-02-eurekaserver</module>\n\n        <module>lab-35-admin-03-adminserver</module>\n        <module>lab-35-admin-03-demo-application</module>\n\n        <module>lab-35-admin-04-adminserver</module>\n        <module>lab-35-admin-05-adminserver</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-35/《芋道 Spring Boot 监控工具 Admin 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Admin/?github>\n"
  },
  {
    "path": "lab-36/lab-36-prometheus-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-36-prometheus-demo</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n\n        <!-- Micrometer 对 Prometheus 的支持 -->\n        <dependency>\n            <groupId>io.micrometer</groupId>\n            <artifactId>micrometer-registry-prometheus</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-36/lab-36-prometheus-demo/src/main/java/cn/iocoder/springboot/lab36/prometheusdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab36.prometheusdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-36/lab-36-prometheus-demo/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application # 应用名\n\nmanagement:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n  metrics:\n    tags: # 通用标签\n      application: ${spring.application.name}\n"
  },
  {
    "path": "lab-36/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-36</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-36-prometheus-demo</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-36/《芋道 Spring Boot 监控平台 Prometheus + Grafana 入门》.md",
    "content": ""
  },
  {
    "path": "lab-37/lab-37-logging-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-37-logging-actuator</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-37/lab-37-logging-actuator/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab37.loggingdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-37/lab-37-logging-actuator/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab37.loggingdemo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/debug\")\n    public void debug() {\n        logger.debug(\"debug\");\n    }\n\n    @GetMapping(\"/info\")\n    public void info() {\n        logger.info(\"info\");\n    }\n\n    @GetMapping(\"/error\")\n    public void error() {\n        logger.error(\"error\");\n    }\n\n}\n"
  },
  {
    "path": "lab-37/lab-37-logging-actuator/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application # 应用名\n\nlogging:\n  # 日志文件配置\n  file:\n#    path: /Users/yunai/logs/ # 日志文件路径。\n    name: /Users/yunai/logs/${spring.application.name}.log # 日志文件名。\n    max-history: 7 # 日志文件要保留的归档的最大天数。默认为 7 天。\n    max-size: 10MB # 日志文件的最大大小。默认为 10MB 。\n\n  # 日志级别\n  level:\n    cn:\n      iocoder:\n        springboot:\n          lab37:\n            loggingdemo:\n              controller: DEBUG\n\nmanagement:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n"
  },
  {
    "path": "lab-37/lab-37-logging-aop/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-37-logging-aop</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring AOP 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-aop</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-37/lab-37-logging-aop/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab37.loggingdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-37/lab-37-logging-aop/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/aspect/HttpAccessAspect.java",
    "content": "package cn.iocoder.springboot.lab37.loggingdemo.aspect;\n\nimport org.aspectj.lang.ProceedingJoinPoint;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.annotation.Aspect;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\n@Aspect\npublic class HttpAccessAspect {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Around(\"@within(org.springframework.stereotype.Controller)\" +\n        \"|| @within(org.springframework.web.bind.annotation.RestController)\")\n    public Object around(ProceedingJoinPoint point) throws Throwable {\n        // 获取类名\n        String className = point.getTarget().getClass().getName();\n        // 获取方法\n        String methodName = point.getSignature().getName();\n        // 记录开始时间\n        long beginTime = System.currentTimeMillis();\n        // 记录返回结果\n        Object result = null;\n        Exception ex = null;\n        try {\n            // 执行方法\n            result = point.proceed();\n            return result;\n        } catch (Exception e) {\n            ex = e;\n            throw e;\n        } finally {\n            // 计算消耗时间\n            long costTime = System.currentTimeMillis() - beginTime;\n            // 发生异常，则打印 ERROR 日志\n            if (ex != null) {\n                logger.error(\"[className: {}][methodName: {}][cost: {} ms][args: {}][发生异常]\",\n                        className, methodName, point.getArgs(), ex);\n            // 正常执行，则打印 INFO 日志\n            } else {\n                logger.info(\"[className: {}][methodName: {}][cost: {} ms][args: {}][return: {}]\",\n                        className, methodName, costTime, point.getArgs(), result);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-37/lab-37-logging-aop/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab37.loggingdemo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/debug\")\n    public void debug() {\n        logger.debug(\"debug\");\n    }\n\n    @GetMapping(\"/info\")\n    public void info() {\n        logger.info(\"info\");\n    }\n\n    @GetMapping(\"/error\")\n    public void error() {\n        logger.error(\"error\");\n    }\n\n}\n"
  },
  {
    "path": "lab-37/lab-37-logging-aop/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application # 应用名\n\nlogging:\n  # 日志文件配置\n  file:\n#    path: /Users/yunai/logs/ # 日志文件路径。\n    name: /Users/yunai/logs/${spring.application.name}.log # 日志文件名。\n    max-history: 7 # 日志文件要保留的归档的最大天数。默认为 7 天。\n    max-size: 10MB # 日志文件的最大大小。默认为 10MB 。\n\n  # 日志级别\n  level:\n    cn:\n      iocoder:\n        springboot:\n          lab37:\n            loggingdemo:\n              controller: DEBUG\n"
  },
  {
    "path": "lab-37/lab-37-logging-debug/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-37-logging-debug</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-37/lab-37-logging-debug/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab37.loggingdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-37/lab-37-logging-debug/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab37.loggingdemo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/debug\")\n    public void debug() {\n        logger.debug(\"debug\");\n    }\n\n    @GetMapping(\"/info\")\n    public void info() {\n        logger.info(\"info\");\n    }\n\n    @GetMapping(\"/error\")\n    public void error() {\n        logger.error(\"error\");\n    }\n\n}\n"
  },
  {
    "path": "lab-37/lab-37-logging-debug/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application # 应用名\n\nlogging:\n  # 日志文件配置\n  file:\n#    path: /Users/yunai/logs/ # 日志文件路径。\n    name: /Users/yunai/logs/${spring.application.name}.log # 日志文件名。\n    max-history: 7 # 日志文件要保留的归档的最大天数。默认为 7 天。\n    max-size: 10MB # 日志文件的最大大小。默认为 10MB 。\n\n# 调试模式\ndebug: true\n"
  },
  {
    "path": "lab-37/lab-37-logging-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-37-logging-demo</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-37/lab-37-logging-demo/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab37.loggingdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-37/lab-37-logging-demo/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab37.loggingdemo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/debug\")\n    public void debug() {\n        logger.debug(\"debug\");\n    }\n\n    @GetMapping(\"/info\")\n    public void info() {\n        logger.info(\"info\");\n    }\n\n    @GetMapping(\"/error\")\n    public void error() {\n        logger.error(\"error\");\n    }\n\n}\n"
  },
  {
    "path": "lab-37/lab-37-logging-demo/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application # 应用名\n\nlogging:\n  # 日志文件配置\n  file:\n#    path: /Users/yunai/logs/ # 日志文件路径。\n    name: /Users/yunai/logs/${spring.application.name}.log # 日志文件名。\n    max-history: 7 # 日志文件要保留的归档的最大天数。默认为 7 天。\n    max-size: 10MB # 日志文件的最大大小。默认为 10MB 。\n\n  # 日志级别\n  level:\n    cn:\n      iocoder:\n        springboot:\n          lab37:\n            loggingdemo:\n              controller: DEBUG\n"
  },
  {
    "path": "lab-37/lab-37-logging-log4j2/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-37-logging-log4j2</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n            <exclusions>\n                <!-- 去掉对 spring-boot-starter-logging 的依赖-->\n                <exclusion>\n                    <groupId>org.springframework.boot</groupId>\n                    <artifactId>spring-boot-starter-logging</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <!-- 实现对 SLF4J + Log4j2 的依赖的引入 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-log4j2</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-37/lab-37-logging-log4j2/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab37.loggingdemo;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    private static Logger logger = LoggerFactory.getLogger(Application.class);\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n\n        // 打印日志\n        logger.debug(\"just do it\");\n    }\n\n}\n"
  },
  {
    "path": "lab-37/lab-37-logging-log4j2/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application # 应用名\n"
  },
  {
    "path": "lab-37/lab-37-logging-log4j2/src/main/resources/log4j2-spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Configuration status=\"WARN\">\n    <Properties>\n        <Property name=\"PID\">????</Property>\n        <Property name=\"LOG_EXCEPTION_CONVERSION_WORD\">%xwEx</Property>\n        <Property name=\"LOG_LEVEL_PATTERN\">%5p</Property>\n        <Property name=\"LOG_DATEFORMAT_PATTERN\">yyyy-MM-dd HH:mm:ss.SSS</Property>\n        <Property name=\"FILE_LOG_BASE_PATH\">/Users/yunai/logs</Property>\n        <Property name=\"APPLICATION_NAME\">demo-application</Property>\n\n        <!-- 控制台的日志格式 -->\n        <Property name=\"CONSOLE_LOG_PATTERN\">%clr{%d{${LOG_DATEFORMAT_PATTERN}}}{faint} %clr{${LOG_LEVEL_PATTERN}} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}</Property>\n        <!-- 日志文件的日志格式 -->\n        <Property name=\"FILE_LOG_PATTERN\">%d{${LOG_DATEFORMAT_PATTERN}} ${LOG_LEVEL_PATTERN} ${sys:PID} --- [%t] %-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD}</Property>\n    </Properties>\n\n    <Appenders>\n        <!-- 控制台的 Appender -->\n        <Console name=\"Console\" target=\"SYSTEM_OUT\" follow=\"true\">\n            <PatternLayout pattern=\"${sys:CONSOLE_LOG_PATTERN}\" />\n        </Console>\n\n        <!-- 日志文件的 Appender -->\n        <RollingFile name=\"File\" fileName=\"${FILE_LOG_BASE_PATH}/${sys:APPLICATION_NAME}\"\n                     filePattern=\"${FILE_LOG_BASE_PATH}/${sys:APPLICATION_NAME}-%d{yyyy-MM-dd-HH}-%i.log.gz\">\n            <!-- 日志的格式化 -->\n            <PatternLayout>\n                <Pattern>${sys:FILE_LOG_PATTERN}</Pattern>\n            </PatternLayout>\n            <!--滚动策略，基于时间 + 大小的分包策略 -->\n            <Policies>\n                <SizeBasedTriggeringPolicy size=\"10 MB\" />\n            </Policies>\n        </RollingFile>\n    </Appenders>\n\n    <Loggers>\n        <!-- 常用组件的 Logger 的日志级别 -->\n        <Logger name=\"org.apache.catalina.startup.DigesterFactory\" level=\"error\" />\n        <Logger name=\"org.apache.catalina.util.LifecycleBase\" level=\"error\" />\n        <Logger name=\"org.apache.coyote.http11.Http11NioProtocol\" level=\"warn\" />\n        <logger name=\"org.apache.sshd.common.util.SecurityUtils\" level=\"warn\"/>\n        <Logger name=\"org.apache.tomcat.util.net.NioSelectorPool\" level=\"warn\" />\n        <Logger name=\"org.eclipse.jetty.util.component.AbstractLifeCycle\" level=\"error\" />\n        <Logger name=\"org.hibernate.validator.internal.util.Version\" level=\"warn\" />\n        <logger name=\"org.springframework.boot.actuate.endpoint.jmx\" level=\"warn\"/>\n\n        <!-- 自定义的 Logger 的日志级别 -->\n        <logger name=\"cn.iocoder.springboot.lab37.loggingdemo\" level=\"debug\"/>\n\n        <!-- 设置 Appender ，同时 ROOT 的日志级别为 INFO -->\n        <Root level=\"info\">\n            <AppenderRef ref=\"Console\" />\n            <AppenderRef ref=\"File\" />\n        </Root>\n    </Loggers>\n</Configuration>\n"
  },
  {
    "path": "lab-37/lab-37-logging-logback/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-37-logging-logback</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-37/lab-37-logging-logback/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab37.loggingdemo;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    private static Logger logger = LoggerFactory.getLogger(Application.class);\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n\n        // 打印日志\n        logger.debug(\"just do it\");\n    }\n\n}\n"
  },
  {
    "path": "lab-37/lab-37-logging-logback/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application # 应用名\n\n"
  },
  {
    "path": "lab-37/lab-37-logging-logback/src/main/resources/logback-spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<configuration>\n\n    <!-- 引入 Spring Boot 默认的 logback XML 配置文件  -->\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n\n    <!-- 控制台 Appender -->\n    <appender name=\"console\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- 日志的格式化 -->\n        <encoder>\n            <pattern>${CONSOLE_LOG_PATTERN}</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <!-- 从 Spring Boot 配置文件中，读取 spring.application.name 应用名 -->\n    <springProperty name=\"applicationName\" scope=\"context\" source=\"spring.application.name\" />\n    <!-- 日志文件的路径 -->\n    <property name=\"LOG_FILE\" value=\"/Users/yunai/logs/${applicationName}.log\"/>​\n    <!-- 日志文件 Appender -->\n    <appender name=\"file\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <file>${LOG_FILE}</file>\n        <!--滚动策略，基于时间 + 大小的分包策略 -->\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>\n            <maxHistory>7</maxHistory>\n            <maxFileSize>10MB</maxFileSize>\n        </rollingPolicy>\n        <!-- 日志的格式化 -->\n        <encoder>\n            <pattern>${FILE_LOG_PATTERN}</pattern>\n            <charset>utf8</charset>\n        </encoder>\n    </appender>\n\n    <!-- 测试环境，独有的配置 -->\n    <springProfile name=\"dev\">\n        <!-- 设置 Appender -->\n        <root level=\"INFO\">\n            <appender-ref ref=\"console\"/>\n        </root>\n\n        <!-- 设置 \"cn.iocoder.springboot.lab37.loggingdemo\" 的 Logger 的日志级别为 DEBUG -->\n        <logger name=\"cn.iocoder.springboot.lab37.loggingdemo\" level=\"DEBUG\"/>\n    </springProfile>\n\n    <!-- 生产环境，独有的配置 -->\n    <springProfile name=\"prod\">\n        <!-- 设置 Appender -->\n        <root level=\"INFO\">\n            <appender-ref ref=\"console\"/>\n            <appender-ref ref=\"file\"/>\n        </root>\n    </springProfile>\n\n</configuration>\n"
  },
  {
    "path": "lab-37/lab-37-logging-multi-env/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-37-logging-multi-env</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-37/lab-37-logging-multi-env/src/main/java/cn/iocoder/springboot/lab37/loggingdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab37.loggingdemo;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    private static Logger logger = LoggerFactory.getLogger(Application.class);\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n\n        // 打印日志\n        logger.debug(\"just do it\");\n    }\n\n}\n"
  },
  {
    "path": "lab-37/lab-37-logging-multi-env/src/main/resources/application-dev.yaml",
    "content": "spring:\n  application:\n    name: demo-application # 应用名\n\nlogging:\n  # 日志级别\n  level:\n    cn:\n      iocoder:\n        springboot:\n          lab37:\n            loggingdemo: DEBUG\n"
  },
  {
    "path": "lab-37/lab-37-logging-multi-env/src/main/resources/application-prod.yaml",
    "content": "spring:\n  application:\n    name: demo-application # 应用名\n\nlogging:\n  # 日志文件配置\n  file:\n    name: /Users/yunai/logs/${spring.application.name}.log # 日志文件名。\n"
  },
  {
    "path": "lab-37/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-37</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-37-logging-demo</module>\n        <module>lab-37-logging-actuator</module>\n        <module>lab-37-logging-logback</module>\n        <module>lab-37-logging-debug</module>\n        <module>lab-37-logging-multi-env</module>\n        <module>lab-37-logging-log4j2</module>\n        <module>lab-37-logging-aop</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-37/《芋道 Spring Boot 日志集成 Logging 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Logging/?github>\n"
  },
  {
    "path": "lab-38/lab-38-elk-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-38-elk-demo</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-38/lab-38-elk-demo/src/main/java/cn/iocoder/springboot/lab38/elkdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab38.elkdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-38/lab-38-elk-demo/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application # 应用名\n\nlogging:\n  # 日志文件配置\n  file:\n    name: /Users/yunai/logs/${spring.application.name}.log # 日志文件名。\n"
  },
  {
    "path": "lab-38/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-38</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-38-elk-demo</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-38/《芋道 Spring Boot 日志平台 ELK + Filebeat 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/ELK/?github>\n"
  },
  {
    "path": "lab-39/lab-39-activemq/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39-activemq</artifactId>\n\n    <dependencies>\n        <!-- 实现对 ActiveMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-activemq</artifactId>\n        </dependency>\n\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-activemq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/ActiveMQApplication.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ActiveMQApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ActiveMQApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-activemq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/consumer/DemoConsumer.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.consumer;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.message.DemoMessage;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jms.annotation.JmsListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class DemoConsumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @JmsListener(destination = DemoMessage.QUEUE)\n    public void onMessage(DemoMessage message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-activemq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.controller;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.producer.DemoProducer;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private DemoProducer producer;\n\n    @GetMapping(\"/activemq\")\n    public String echo() {\n        this.sendMessage(1);\n        return \"activemq\";\n    }\n\n    public void sendMessage(Integer id) {\n        producer.syncSend(id);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-activemq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/message/DemoMessage.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.message;\n\nimport java.io.Serializable;\n\npublic class DemoMessage implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public DemoMessage setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"DemoMessage{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-activemq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/producer/DemoProducer.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.producer;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.message.DemoMessage;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jms.core.JmsMessagingTemplate;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class DemoProducer {\n\n    @Autowired\n    private JmsMessagingTemplate jmsTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 DemoMessage 消息\n        DemoMessage message = new DemoMessage();\n        message.setId(id);\n        // 同步发送消息\n        jmsTemplate.convertAndSend(DemoMessage.QUEUE, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-activemq/src/main/resources/application.yaml",
    "content": "server:\n  port: 8079\n\nspring:\n  # ActiveMQ 配置项，对应 ActiveMQProperties 配置类\n  activemq:\n    broker-url: tcp://127.0.0.1:61616 # Activemq Broker 的地址\n    user: admin # 账号\n    password: admin # 密码\n    packages:\n      trust-all: true # 可信任的反序列化包\n"
  },
  {
    "path": "lab-39/lab-39-async/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39-async</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-async/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/AsyncApplication.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.scheduling.annotation.EnableAsync;\n\n@SpringBootApplication\n@EnableAsync(proxyTargetClass = true) // 开启 @Async 的支持\npublic class AsyncApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AsyncApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-async/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.controller;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.service.DemoService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private DemoService demoService;\n\n    @GetMapping(\"/async\")\n    public String echo() {\n        demoService.async();\n        return \"async\";\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-async/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/service/DemoService.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.service;\n\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class DemoService {\n\n    @Async\n    public void async() {\n        System.out.println(\"异步任务的执行\");\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-async/src/main/resources/application.yml",
    "content": "server:\n  port: 8079\n"
  },
  {
    "path": "lab-39/lab-39-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n    <packaging>jar</packaging>\n\n    <artifactId>lab-39-demo</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.boot</groupId>-->\n<!--            <artifactId>spring-boot-loader</artifactId>-->\n<!--        </dependency>-->\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <configuration>\n                    <fork>true</fork>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-demo/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\nimport java.io.IOException;\nimport java.net.URISyntaxException;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) throws URISyntaxException, IOException {\n//        ProtectionDomain protectionDomain = Launcher.class.getProtectionDomain();\n//        CodeSource codeSource = protectionDomain.getCodeSource();\n//        URI location = (codeSource != null) ? codeSource.getLocation().toURI() : null;\n//        String path = (location != null) ? location.getSchemeSpecificPart() : null;\n//        if (path == null) {\n//            throw new IllegalStateException(\"Unable to determine code source archive\");\n//        }\n//        File root = new File(path);\n//        System.out.println(root + \"\" + root.isDirectory());\n//        // 创建 Archive 对象\n//        Archive archive = new JarFileArchive(root);\n//        // 创建 EntryFilter 对象\n//        Archive.EntryFilter filter = new Archive.EntryFilter() {\n//\n//            static final String BOOT_INF_CLASSES = \"BOOT-INF/classes/\";\n//\n//            static final String BOOT_INF_LIB = \"BOOT-INF/lib/\";\n//\n//            @Override\n//            public boolean matches(Archive.Entry entry) {\n//                // 如果是目录的情况，只要 BOOT-INF/classes/ 目录\n//                if (entry.isDirectory()) {\n//                    return entry.getName().equals(BOOT_INF_CLASSES);\n//                }\n//                // 如果是文件的情况，只要 BOOT-INF/lib/ 目录下的 `jar` 包\n//                return entry.getName().startsWith(BOOT_INF_LIB);\n//            }\n//\n//        };\n//        // 执行读取\n//        for (Archive item : archive.getNestedArchives(filter)) {\n//            System.out.println(item.getUrl());\n//        }\n\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-demo/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return \"echo\";\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-demo/src/main/resources/application.yml",
    "content": "server:\n  port: 8079\n"
  },
  {
    "path": "lab-39/lab-39-elasticsearch/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39-elasticsearch</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 自动化配置 Spring Data Elasticsearch -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-elasticsearch/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/ElasticsearchApplication.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ElasticsearchApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ElasticsearchApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-elasticsearch/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.controller;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.dataobject.ESUserDO;\nimport cn.iocoder.springboot.lab39.skywalkingdemo.repository.ESUserRepository;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\n    public class DemoController {\n\n    @Autowired\n    private ESUserRepository userRepository;\n\n    @GetMapping(\"/elasticsearch\")\n    public String mysql() {\n        this.findById(1);\n        return \"elasticsearch\";\n    }\n\n    public ESUserDO findById(Integer id) {\n        return userRepository.findById(id).orElse(null);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-elasticsearch/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/dataobject/ESUserDO.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.dataobject;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.elasticsearch.annotations.Document;\n\nimport java.util.Date;\n\n@Document(indexName = \"user\", // 索引名\n        type = \"user\", // 类型。未来的版本即将废弃\n        shards = 1, // 默认索引分区数\n        replicas = 0, // 每个分区的备份数\n        refreshInterval = \"-1\" // 刷新间隔\n)\npublic class ESUserDO {\n\n    @Id\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                \", createTime=\" + createTime +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-elasticsearch/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/repository/ESUserRepository.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.repository;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.dataobject.ESUserDO;\nimport org.springframework.data.elasticsearch.repository.ElasticsearchRepository;\n\npublic interface ESUserRepository extends ElasticsearchRepository<ESUserDO, Integer> {\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-elasticsearch/src/main/resources/application.yml",
    "content": "server:\n  port: 8079\n\nspring:\n  data:\n    # Elasticsearch 配置项\n    elasticsearch:\n      cluster-name: elasticsearch2 # 集群名\n      cluster-nodes: 127.0.0.1:9301 # 集群节点\n"
  },
  {
    "path": "lab-39/lab-39-elasticsearch-jest/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39-elasticsearch-jest</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 自动化配置 Spring Data Jest -->\n        <dependency>\n            <groupId>com.github.vanroy</groupId>\n            <artifactId>spring-boot-starter-data-jest</artifactId>\n            <version>3.3.0.RELEASE</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-elasticsearch-jest/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/ElasticsearchJestApplication.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration;\nimport org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration;\n\n@SpringBootApplication(exclude = {ElasticsearchAutoConfiguration.class, ElasticsearchDataAutoConfiguration.class})\npublic class ElasticsearchJestApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ElasticsearchJestApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-elasticsearch-jest/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.controller;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.dataobject.ESUserDO;\nimport cn.iocoder.springboot.lab39.skywalkingdemo.repository.ESUserRepository;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Date;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private ESUserRepository userRepository;\n\n    @GetMapping(\"/elasticsearch\")\n    public String mysql() {\n        this.save(1);\n        this.findById(1);\n        return \"elasticsearch\";\n    }\n\n    public void save(Integer id) {\n        ESUserDO user = new ESUserDO();\n        user.setId(id);\n        user.setUsername(\"username：\" + id);\n        user.setPassword(\"password：\" + id);\n        user.setCreateTime(new Date());\n        userRepository.save(user);\n    }\n\n    public ESUserDO findById(Integer id) {\n        return userRepository.findById(id).orElse(null);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-elasticsearch-jest/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/dataobject/ESUserDO.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.dataobject;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.elasticsearch.annotations.Document;\n\nimport java.util.Date;\n\n@Document(indexName = \"user\", // 索引名\n        type = \"user\", // 类型。未来的版本即将废弃\n        shards = 1, // 默认索引分区数\n        replicas = 0, // 每个分区的备份数\n        refreshInterval = \"-1\" // 刷新间隔\n)\npublic class ESUserDO {\n\n    @Id\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public ESUserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public ESUserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public ESUserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n    public Date getCreateTime() {\n        return createTime;\n    }\n\n    public ESUserDO setCreateTime(Date createTime) {\n        this.createTime = createTime;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                \", createTime=\" + createTime +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-elasticsearch-jest/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/repository/ESUserRepository.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.repository;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.dataobject.ESUserDO;\nimport org.springframework.data.elasticsearch.repository.ElasticsearchRepository;\n\npublic interface ESUserRepository extends ElasticsearchRepository<ESUserDO, Integer> {\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-elasticsearch-jest/src/main/resources/application.yml",
    "content": "server:\n  port: 8079\n\nspring:\n  data:\n    # Jest 配置项\n    jest:\n      uri: http://127.0.0.1:9400\n\n"
  },
  {
    "path": "lab-39/lab-39-kafka/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.11.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39-kafka</artifactId>\n\n    <dependencies>\n        <!-- 引入 Spring-Kafka 依赖 -->\n        <!-- 已经内置 kafka-clients 依赖，所以无需重复引入 -->\n        <dependency>\n            <groupId>org.springframework.kafka</groupId>\n            <artifactId>spring-kafka</artifactId>\n            <version>2.2.11.RELEASE</version>\n        </dependency>\n\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-kafka/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/KafkaApplication.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class KafkaApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(KafkaApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-kafka/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/consumer/DemoConsumer.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.consumer;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.message.DemoMessage;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.kafka.annotation.KafkaListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class DemoConsumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @KafkaListener(topics = DemoMessage.TOPIC,\n            groupId = \"demo-consumer-group-\" + DemoMessage.TOPIC)\n    public void onMessage(DemoMessage message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-kafka/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.controller;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.producer.DemoProducer;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.concurrent.ExecutionException;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private DemoProducer producer;\n\n    @GetMapping(\"/kafka\")\n    public String echo() throws ExecutionException, InterruptedException {\n        this.sendMessage(1);\n        return \"kafka\";\n    }\n\n    public void sendMessage(Integer id) throws ExecutionException, InterruptedException {\n        producer.syncSend(id);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-kafka/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/message/DemoMessage.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.message;\n\n/**\n * 示例 Message 消息\n */\npublic class DemoMessage {\n\n    public static final String TOPIC = \"DEMO\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public DemoMessage setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"DemoMessage{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-kafka/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/producer/DemoProducer.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.producer;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.message.DemoMessage;\nimport org.springframework.kafka.core.KafkaTemplate;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.concurrent.ExecutionException;\n\n@Component\npublic class DemoProducer {\n\n    @Resource\n    private KafkaTemplate<Object, Object> kafkaTemplate;\n\n    public SendResult syncSend(Integer id) throws ExecutionException, InterruptedException {\n        // 创建 DemoMessage 消息\n        DemoMessage message = new DemoMessage();\n        message.setId(id);\n        // 同步发送消息\n        return kafkaTemplate.send(DemoMessage.TOPIC, message).get();\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-kafka/src/main/resources/application.yaml",
    "content": "server:\n  port: 8079\n\nspring:\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n    # Kafka Producer 配置项\n    producer:\n      acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。\n      retries: 3 # 发送失败时，重试发送的次数\n      key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化\n      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化\n    # Kafka Consumer 配置项\n    consumer:\n      auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解\n      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer\n      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer\n      properties:\n        spring:\n          json:\n            trusted:\n              packages: cn.iocoder.springboot.lab39.skywalkingdemo.message # 消息 POJO 可信目录，解决 JSON 无法反序列化的问题\n    # Kafka Consumer Listener 监听器配置\n    listener:\n      missing-topics-fatal: false # 消费监听接口监听的主题不存在时，默认会报错。所以通过设置为 false ，解决报错\n"
  },
  {
    "path": "lab-39/lab-39-logback/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39-logback</artifactId>\n\n    <dependencies>\n        <!-- SkyWalking 对 Logback 的集成 -->\n        <dependency>\n            <groupId>org.apache.skywalking</groupId>\n            <artifactId>apm-toolkit-logback-1.x</artifactId>\n            <version>6.6.0</version>\n        </dependency>\n\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-logback/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/LogbackApplication.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class LogbackApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(LogbackApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-logback/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/logback\")\n    public String echo() {\n        logger.info(\"测试日志\");\n        return \"logback\";\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-logback/src/main/resources/application.yml",
    "content": "server:\n  port: 8079\n\nspring:\n  application:\n    name: demo-application-logback\n"
  },
  {
    "path": "lab-39/lab-39-logback/src/main/resources/logback-spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<configuration>\n\n    <!-- 引入 Spring Boot 默认的 logback XML 配置文件  -->\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\"/>\n\n    <!-- 控制台 Appender -->\n    <property name=\"CONSOLE_LOG_PATTERN\" value=\"%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %tid %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}\"/>\n    <appender name=\"console\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- 日志的格式化 -->\n        <encoder class=\"ch.qos.logback.core.encoder.LayoutWrappingEncoder\">\n            <layout class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout\">\n                <Pattern>${CONSOLE_LOG_PATTERN}</Pattern>\n            </layout>\n        </encoder>\n    </appender>\n\n    <!-- 从 Spring Boot 配置文件中，读取 spring.application.name 应用名 -->\n    <springProperty name=\"applicationName\" scope=\"context\" source=\"spring.application.name\" />\n    <property name=\"FILE_LOG_PATTERN\" value=\"%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } %tid --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}\"/>\n    <!-- 日志文件的路径 -->\n    <property name=\"LOG_FILE\" value=\"/Users/yunai/logs/${applicationName}.log\"/>​\n    <!-- 日志文件 Appender -->\n    <appender name=\"file\" class=\"ch.qos.logback.core.rolling.RollingFileAppender\">\n        <file>${LOG_FILE}</file>\n        <!--滚动策略，基于时间 + 大小的分包策略 -->\n        <rollingPolicy class=\"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy\">\n            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>\n            <maxHistory>7</maxHistory>\n            <maxFileSize>10MB</maxFileSize>\n        </rollingPolicy>\n        <!-- 日志的格式化 -->\n        <encoder class=\"ch.qos.logback.core.encoder.LayoutWrappingEncoder\">\n            <layout class=\"org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout\">\n                <Pattern>${FILE_LOG_PATTERN}</Pattern>\n            </layout>\n        </encoder>\n    </appender>\n\n    <!-- 设置 Appender -->\n    <root level=\"INFO\">\n        <appender-ref ref=\"console\"/>\n        <appender-ref ref=\"file\"/>\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "lab-39/lab-39-mongodb/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39-mongodb</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 自动化配置 Spring Data Mongodb -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-mongodb</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-mongodb/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/MongoDBApplication.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class MongoDBApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(MongoDBApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-mongodb/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.controller;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.dataobject.UserDO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.mongodb.core.MongoTemplate;\nimport org.springframework.data.mongodb.core.query.Criteria;\nimport org.springframework.data.mongodb.core.query.Query;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private MongoTemplate mongoTemplate;\n\n    @GetMapping(\"/mongodb\")\n    public String mysql() {\n        this.findById(1);\n        return \"mongodb\";\n    }\n\n    public UserDO findById(Integer id) {\n        return mongoTemplate.findOne(new Query(Criteria.where(\"_id\").is(id)), UserDO.class);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-mongodb/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.dataobject;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.mongodb.core.mapping.Document;\n\nimport java.util.Date;\n\n/**\n * 用户 DO\n */\n@Document(collection = \"User\")\npublic class UserDO {\n\n    @Id\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                \", createTime=\" + createTime +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-mongodb/src/main/resources/application.yml",
    "content": "server:\n  port: 8079\n\nspring:\n  data:\n    # MongoDB 配置项，对应 MongoProperties 类\n    mongodb:\n      host: 127.0.0.1\n      port: 27017\n      database: yourdatabase\n      username: test01\n      password: password01\n      # 上述属性，也可以只配置 uri\n"
  },
  {
    "path": "lab-39/lab-39-mysql/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39-mysql</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.46</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-mysql/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/MySQLApplication.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class MySQLApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(MySQLApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-mysql/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.BeanPropertyRowMapper;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private JdbcTemplate template;\n\n    @GetMapping(\"/mysql\")\n    public String mysql() {\n        this.selectById(1);\n        return \"mysql\";\n    }\n\n    public Object selectById(Integer id) {\n        return template.queryForObject(\"SELECT id, username, password FROM t_user WHERE id = ?\",\n                new BeanPropertyRowMapper<>(Object.class), // 结果转换成对应的对象。Object 理论来说是 UserDO.class ，这里偷懒了。\n                id);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-mysql/src/main/resources/application.yml",
    "content": "server:\n  port: 8079\n\nspring:\n  # datasource 数据源配置内容\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/lab-39-mysql?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n"
  },
  {
    "path": "lab-39/lab-39-opentracing/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39-opentracing</artifactId>\n\n    <dependencies>\n        <!-- SkyWalking Opentracing 集成 -->\n        <dependency>\n            <groupId>org.apache.skywalking</groupId>\n            <artifactId>apm-toolkit-opentracing</artifactId>\n            <version>6.6.0</version>\n        </dependency>\n\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-opentracing/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/OpentracingApplication.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class OpentracingApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(OpentracingApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-opentracing/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.controller;\n\nimport io.opentracing.Tracer;\nimport org.apache.skywalking.apm.toolkit.opentracing.SkywalkingTracer;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @GetMapping(\"/opentracing\")\n    public String echo() {\n        // 创建一个 Span\n        Tracer tracer = new SkywalkingTracer();\n        tracer.buildSpan(\"custom_operation\").withTag(\"mp\", \"芋道源码\").startManual().finish();\n\n        // 返回\n        return \"opentracing\";\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-opentracing/src/main/resources/application.yml",
    "content": "server:\n  port: 8079\n"
  },
  {
    "path": "lab-39/lab-39-rabbitmq-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39-rabbitmq-demo</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RabbitMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/RabbitMQApplication.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class RabbitMQApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(RabbitMQApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/config/RabbitConfig.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.config;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.message.DemoMessage;\nimport org.springframework.amqp.core.Binding;\nimport org.springframework.amqp.core.BindingBuilder;\nimport org.springframework.amqp.core.DirectExchange;\nimport org.springframework.amqp.core.Queue;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class RabbitConfig {\n\n    // 创建 Queue\n    @Bean\n    public Queue demoQueue() {\n        return new Queue(DemoMessage.QUEUE, // Queue 名字\n                true, // durable: 是否持久化\n                false, // exclusive: 是否排它\n                false); // autoDelete: 是否自动删除\n    }\n\n    // 创建 Direct Exchange\n    @Bean\n    public DirectExchange demoExchange() {\n        return new DirectExchange(DemoMessage.EXCHANGE,\n                true,  // durable: 是否持久化\n                false);  // exclusive: 是否排它\n    }\n\n    // 创建 Binding\n    // Exchange：DemoMessage.EXCHANGE\n    // Routing key：DemoMessage.ROUTING_KEY\n    // Queue：DemoMessage.QUEUE\n    @Bean\n    public Binding demoBinding() {\n        return BindingBuilder.bind(demoQueue()).to(demoExchange()).with(DemoMessage.ROUTING_KEY);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/consumer/DemoConsumer.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.consumer;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.message.DemoMessage;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(queues = DemoMessage.QUEUE)\npublic class DemoConsumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(DemoMessage message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.controller;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.producer.DemoProducer;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private DemoProducer producer;\n\n    @GetMapping(\"/rabbitmq\")\n    public String echo() {\n        this.sendMessage(1);\n        return \"rabbitmq\";\n    }\n\n    public void sendMessage(Integer id) {\n        producer.syncSend(id);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/message/DemoMessage.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.message;\n\nimport java.io.Serializable;\n\npublic class DemoMessage implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_\";\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_\";\n\n    public static final String ROUTING_KEY = \"ROUTING_KEY_\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public DemoMessage setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"DemoMessage{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-rabbitmq-demo/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/producer/DemoProducer.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.producer;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.message.DemoMessage;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class DemoProducer {\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 DemoMessage 消息\n        DemoMessage message = new DemoMessage();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(DemoMessage.EXCHANGE, DemoMessage.ROUTING_KEY, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-rabbitmq-demo/src/main/resources/application.yaml",
    "content": "server:\n  port: 8079\n\nspring:\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n"
  },
  {
    "path": "lab-39/lab-39-redis/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39-redis</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Data Redis 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis</artifactId>\n            <exclusions>\n                <!-- 去掉对 Lettuce 的依赖，因为 Spring Boot 优先使用 Lettuce 作为 Redis 客户端 -->\n                <exclusion>\n                    <groupId>io.lettuce</groupId>\n                    <artifactId>lettuce-core</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <!-- 引入 Jedis 的依赖，这样 Spring Boot 实现对 Jedis 的自动化配置 -->\n        <dependency>\n            <groupId>redis.clients</groupId>\n            <artifactId>jedis</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-redis/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/RedisApplication.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class RedisApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(RedisApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-redis/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private StringRedisTemplate redisTemplate;\n\n    @GetMapping(\"/redis\")\n    public String redis() {\n        this.get(\"demo\");\n        return \"redis\";\n    }\n\n    public void get(String key) {\n        redisTemplate.opsForValue().get(key);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-redis/src/main/resources/application.yml",
    "content": "server:\n  port: 8079\n\nspring:\n  # 对应 RedisProperties 类\n  redis:\n    host: 127.0.0.1\n    port: 6379\n    password: # Redis 服务器密码，默认为空。生产中，一定要设置 Redis 密码！\n    database: 0 # Redis 数据库号，默认为 0 。\n    timeout: 0 # Redis 连接超时时间，单位：毫秒。\n    # 对应 RedisProperties.Jedis 内部类\n    jedis:\n      pool:\n        max-active: 8 # 连接池最大连接数，默认为 8 。使用负数表示没有限制。\n        max-idle: 8 # 默认连接数最小空闲的连接数，默认为 8 。使用负数表示没有限制。\n        min-idle: 0 # 默认连接池最小空闲的连接数，默认为 0 。允许设置 0 和 正数。\n        max-wait: -1 # 连接池最大阻塞等待时间，单位：毫秒。默认为 -1 ，表示不限制。\n"
  },
  {
    "path": "lab-39/lab-39-rocketmq/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39-rocketmq</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 RocketMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.rocketmq</groupId>\n            <artifactId>rocketmq-spring-boot-starter</artifactId>\n            <version>2.0.4</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-rocketmq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/rocketmqdemo/RocketMQApplication.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.rocketmqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class RocketMQApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(RocketMQApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-rocketmq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/rocketmqdemo/consumer/DemoConsumer.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.rocketmqdemo.consumer;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.rocketmqdemo.message.DemoMessage;\nimport org.apache.rocketmq.spring.annotation.RocketMQMessageListener;\nimport org.apache.rocketmq.spring.core.RocketMQListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RocketMQMessageListener(\n        topic = DemoMessage.TOPIC,\n        consumerGroup = \"demo-consumer-group-\" + DemoMessage.TOPIC\n)\npublic class DemoConsumer implements RocketMQListener<DemoMessage> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void onMessage(DemoMessage message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-rocketmq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/rocketmqdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.rocketmqdemo.controller;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.rocketmqdemo.producer.DemoProducer;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private DemoProducer producer;\n\n    @GetMapping(\"/rocketmq\")\n    public String echo() {\n        this.sendMessage(1);\n        return \"rocketmq\";\n    }\n\n    public void sendMessage(Integer id) {\n        producer.syncSend(id);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-rocketmq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/rocketmqdemo/message/DemoMessage.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.rocketmqdemo.message;\n\n/**\n * 示例的 Message 消息\n */\npublic class DemoMessage {\n\n    public static final String TOPIC = \"DEMO\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public DemoMessage setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"DemoMessage{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-rocketmq/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/rocketmqdemo/producer/DemoProducer.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.rocketmqdemo.producer;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.rocketmqdemo.message.DemoMessage;\nimport org.apache.rocketmq.client.producer.SendResult;\nimport org.apache.rocketmq.spring.core.RocketMQTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class DemoProducer {\n\n    @Autowired\n    private RocketMQTemplate rocketMQTemplate;\n\n    public SendResult syncSend(Integer id) {\n        // 创建 DemoMessage 消息\n        DemoMessage message = new DemoMessage();\n        message.setId(id);\n        // 同步发送消息\n        return rocketMQTemplate.syncSend(DemoMessage.TOPIC, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-rocketmq/src/main/resources/application.yaml",
    "content": "server:\n  port: 8079\n\n# rocketmq 配置项，对应 RocketMQProperties 配置类\nrocketmq:\n  name-server: 127.0.0.1:9876 # RocketMQ Namesrv\n  # Producer 配置项\n  producer:\n    group: demo-producer-group # 生产者分组\n    send-message-timeout: 3000 # 发送消息超时时间，单位：毫秒。默认为 3000 。\n"
  },
  {
    "path": "lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-39-skywalking-dubbo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39-skywalking-dubbo-api</artifactId>\n\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-api/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/api/UserService.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.api;\n\n/**\n * 用户服务 RPC Service 接口\n */\npublic interface UserService {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    String get(Integer id);\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-api/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/package-info.java",
    "content": "package cn.iocoder.springcloud.labx13;\n"
  },
  {
    "path": "lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39-skywalking-dubbo-consumer</artifactId>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-39-skywalking-dubbo-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Dubbo 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n        <!-- 使用 Zookeeper 作为注册中心 -->\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-framework</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-recipes</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-consumer/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.consumerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-consumer/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/consumerdemo/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.consumerdemo.controller;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.api.UserService;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Reference(protocol = \"dubbo\", version = \"1.0.0\")\n    private UserService userService;\n\n    @GetMapping(\"/get\")\n    public String  get(@RequestParam(\"id\") Integer id) {\n        return userService.get(id);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-consumer/src/main/resources/application.yaml",
    "content": "server:\n  port: 8079\n\nspring:\n  application:\n    name: user-service-consumer\n\n# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: ${spring.application.name} # 应用名\n  # Dubbo 注册中心配置\n  registry:\n    address: zookeeper://127.0.0.1:2181 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n"
  },
  {
    "path": "lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39-skywalking-dubbo-provider</artifactId>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-39-skywalking-dubbo-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Boot 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对 Dubbo 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n        <!-- 使用 Zookeeper 作为注册中心 -->\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-framework</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-recipes</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-provider/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/providerdemo/ProviderApplication.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.providerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProviderApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-provider/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/providerdemo/service/UserServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.providerdemo.service;\n\nimport cn.iocoder.springboot.lab39.skywalkingdemo.api.UserService;\n\n@org.apache.dubbo.config.annotation.Service(version = \"1.0.0\")\npublic class UserServiceImpl implements UserService {\n\n    @Override\n    public String get(Integer id) {\n        return \"user:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-skywalking-dubbo/lab-39-skywalking-dubbo-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: user-service-provider\n\n# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: ${spring.application.name} # 应用名\n  # Dubbo 注册中心配\n  registry:\n    address: zookeeper://127.0.0.1:2181 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springboot.lab39.skywalkingdemo.providerdemo.service\n"
  },
  {
    "path": "lab-39/lab-39-skywalking-dubbo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-39</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39-skywalking-dubbo</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>lab-39-skywalking-dubbo-api</module>\n        <module>lab-39-skywalking-dubbo-provider</module>\n        <module>lab-39-skywalking-dubbo-consumer</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-springmvc/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39-springmvc</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-springmvc/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-springmvc/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return \"echo\";\n    }\n\n    @GetMapping(\"/hello\")\n    public String hello() {\n        return \"hello\";\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-springmvc/src/main/resources/application.yml",
    "content": "server:\n  port: 8079\n"
  },
  {
    "path": "lab-39/lab-39-trace-annotations/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39-trace-annotations</artifactId>\n\n    <dependencies>\n        <!-- SkyWalking 工具类 -->\n        <dependency>\n            <groupId>org.apache.skywalking</groupId>\n            <artifactId>apm-toolkit-trace</artifactId>\n            <version>6.6.0</version>\n        </dependency>\n\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-39/lab-39-trace-annotations/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/TraceAnnotationsApplication.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class TraceAnnotationsApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(TraceAnnotationsApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-trace-annotations/src/main/java/cn/iocoder/springboot/lab39/skywalkingdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab39.skywalkingdemo.controller;\n\nimport org.apache.skywalking.apm.toolkit.trace.ActiveSpan;\nimport org.apache.skywalking.apm.toolkit.trace.Trace;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @GetMapping(\"/trace_annotations\")\n    @Trace(operationName = \"trace_annotations\")\n    public String echo() {\n        // 自定义 SkyWalking Span\n        ActiveSpan.tag(\"mp\", \"芋道源码\");\n\n        // 返回\n        return \"trace_annotations\";\n    }\n\n}\n"
  },
  {
    "path": "lab-39/lab-39-trace-annotations/src/main/resources/application.yml",
    "content": "server:\n  port: 8079\n"
  },
  {
    "path": "lab-39/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-39</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-39-demo</module>\n        <module>lab-39-springmvc</module>\n        <module>lab-39-mysql</module>\n        <module>lab-39-redis</module>\n        <module>lab-39-mongodb</module>\n        <module>lab-39-elasticsearch</module>\n        <module>lab-39-elasticsearch-jest</module>\n        <module>lab-39-rocketmq</module>\n        <module>lab-39-kafka</module>\n        <module>lab-39-rabbitmq-demo</module>\n        <module>lab-39-activemq</module>\n        <module>lab-39-logback</module>\n        <module>lab-39-trace-annotations</module>\n        <module>lab-39-opentracing</module>\n        <module>lab-39-async</module>\n        <module>lab-39-skywalking-dubbo</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-39/《芋道 Spring Boot 链路追踪 SkyWalking 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/SkyWalking/?github>\n"
  },
  {
    "path": "lab-40/lab-40-activemq/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-40-activemq</artifactId>\n\n    <dependencies>\n        <!-- 实现对 ActiveMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-activemq</artifactId>\n        </dependency>\n\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- Brave 核心库 -->\n        <!-- The below are needed to report traces to http://localhost:9411/api/v2/spans -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.zipkin.reporter2</groupId>\n            <artifactId>zipkin-sender-okhttp3</artifactId>\n        </dependency>\n\n        <!-- Adds the MVC class and method names to server spans -->\n        <!-- Brave 对 Spring MVC 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-spring-webmvc</artifactId>\n        </dependency>\n        <!-- Brave 对 JMS 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-jms</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <dependencyManagement>\n        <!-- Brave Bom 文件 -->\n        <dependencies>\n            <dependency>\n                <groupId>io.zipkin.brave</groupId>\n                <artifactId>brave-bom</artifactId>\n                <version>5.9.1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n</project>\n"
  },
  {
    "path": "lab-40/lab-40-activemq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/ActiveMQApplication.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ActiveMQApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ActiveMQApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-activemq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\n@Import(SpanCustomizingAsyncHandlerInterceptor.class) // 创建拦截器 SpanCustomizingAsyncHandlerInterceptor Bean\npublic class SpringMvcConfiguration implements WebMvcConfigurer {\n\n    @Autowired\n    public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;\n\n    /**\n     * Decorates server spans with application-defined web tags\n     */\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) { // 记录 SpringMVC 相关信息到 Span 中\n        registry.addInterceptor(webMvcTracingCustomizer);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-activemq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.CurrentSpanCustomizer;\nimport brave.SpanCustomizer;\nimport brave.Tracing;\nimport brave.http.HttpTracing;\nimport brave.jms.JmsTracing;\nimport brave.servlet.TracingFilter;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport zipkin2.Span;\nimport zipkin2.reporter.AsyncReporter;\nimport zipkin2.reporter.Sender;\nimport zipkin2.reporter.okhttp3.OkHttpSender;\n\nimport javax.jms.ConnectionFactory;\nimport javax.servlet.Filter;\n\n@Configuration\npublic class ZipkinConfiguration {\n\n    // ==================== 通用配置 ====================\n\n    /**\n     * Configuration for how to send spans to Zipkin\n     */\n    @Bean\n    public Sender sender() { // Sender 采用 HTTP 通信方式\n        return OkHttpSender.create(\"http://127.0.0.1:9411/api/v2/spans\");\n    }\n\n    /**\n     * Configuration for how to buffer spans into messages for Zipkin\n     */\n    @Bean\n    public AsyncReporter<Span> spanReporter() { // 异步 Reporter\n        return AsyncReporter.create(sender());\n    }\n\n    /**\n     * Controls aspects of tracing such as the service name that shows up in the UI\n     */\n    @Bean\n    public Tracing tracing(@Value(\"${spring.application.name}\") String serviceName) {\n        return Tracing.newBuilder()\n                .localServiceName(serviceName) // 应用名\n                .spanReporter(this.spanReporter()).build();\n    }\n\n    /**\n     * Allows someone to add tags to a span if a trace is in progress\n     */\n    @Bean\n    public SpanCustomizer spanCustomizer(Tracing tracing) {\n        return CurrentSpanCustomizer.create(tracing);\n    }\n\n    // ==================== HTTP 相关 ====================\n\n    /**\n     * Decides how to name and tag spans. By default they are named the same as the http method\n     */\n    @Bean\n    public HttpTracing httpTracing(Tracing tracing) {\n        return HttpTracing.create(tracing);\n    }\n\n    /**\n     * Creates server spans for http requests\n     */\n    @Bean\n    public Filter tracingFilter(HttpTracing httpTracing) { // 拦截请求，记录 HTTP 请求的链路信息\n        return TracingFilter.create(httpTracing);\n    }\n\n    // ==================== SpringMVC 相关 ====================\n    // @see SpringMvcConfiguration 类上的，@Import(SpanCustomizingAsyncHandlerInterceptor.class) 。因为 SpanCustomizingAsyncHandlerInterceptor 未提供 public 构造方法\n\n    // ==================== RabbitMQ 相关 ====================\n\n    @Bean\n    public JmsTracing jmsTracing(Tracing tracing) {\n        return JmsTracing.newBuilder(tracing)\n                .remoteServiceName(\"demo-mq-activemq\") // 远程 ActiveMQ 服务名，可自定义\n                .build();\n    }\n\n    @Bean\n    public BeanPostProcessor activeMQBeanPostProcessor(JmsTracing jmsTracing) {\n        return new BeanPostProcessor() {\n\n            @Override\n            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {\n                return bean;\n            }\n\n            @Override\n            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n                // 如果是 ConnectionFactory ，针对 ActiveMQ Producer 和 Consumer\n                if (bean instanceof ConnectionFactory) {\n                    return jmsTracing.connectionFactory((ConnectionFactory) bean);\n                }\n                return bean;\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-activemq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/consumer/DemoConsumer.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.consumer;\n\nimport cn.iocoder.springboot.lab40.zipkindemo.message.DemoMessage;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jms.annotation.JmsListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class DemoConsumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @JmsListener(destination = DemoMessage.QUEUE)\n    public void onMessage(DemoMessage message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-activemq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.controller;\n\nimport cn.iocoder.springboot.lab40.zipkindemo.producer.DemoProducer;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private DemoProducer producer;\n\n    @GetMapping(\"/activemq\")\n    public String echo() {\n        this.sendMessage(1);\n        return \"activemq\";\n    }\n\n    public void sendMessage(Integer id) {\n        producer.syncSend(id);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-activemq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/message/DemoMessage.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.message;\n\nimport java.io.Serializable;\n\npublic class DemoMessage implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public DemoMessage setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"DemoMessage{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-activemq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/producer/DemoProducer.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.producer;\n\nimport cn.iocoder.springboot.lab40.zipkindemo.message.DemoMessage;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jms.core.JmsMessagingTemplate;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class DemoProducer {\n\n    @Autowired\n    private JmsMessagingTemplate jmsTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 DemoMessage 消息\n        DemoMessage message = new DemoMessage();\n        message.setId(id);\n        // 同步发送消息\n        jmsTemplate.convertAndSend(DemoMessage.QUEUE, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-activemq/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application-activemq\n\n  # ActiveMQ 配置项，对应 ActiveMQProperties 配置类\n  activemq:\n    broker-url: tcp://127.0.0.1:61616 # Activemq Broker 的地址\n    user: admin # 账号\n    password: admin # 密码\n    packages:\n      trust-all: true # 可信任的反序列化包\n"
  },
  {
    "path": "lab-40/lab-40-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-40-demo</artifactId>\n    <packaging>jar</packaging>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- HttpClient 库  -->\n        <dependency>\n            <groupId>org.apache.httpcomponents</groupId>\n            <artifactId>httpclient</artifactId>\n        </dependency>\n\n        <!-- Brave 核心库 -->\n        <!-- The below are needed to report traces to http://localhost:9411/api/v2/spans -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.zipkin.reporter2</groupId>\n            <artifactId>zipkin-sender-okhttp3</artifactId>\n        </dependency>\n\n        <!-- Adds the MVC class and method names to server spans -->\n        <!-- Brave 对 Spring MVC 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-spring-webmvc</artifactId>\n        </dependency>\n        <!-- Instruments the underlying HttpClient requests that call the backend -->\n        <!-- Brave 对 HttpClient 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-httpclient</artifactId>\n        </dependency>\n        <!-- Integrates so you can use log patterns like %X{traceId}/%X{spanId} -->\n        <!-- Brave 对 SLF4J 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-context-slf4j</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <dependencyManagement>\n        <!-- Brave Bom 文件 -->\n        <dependencies>\n            <dependency>\n                <groupId>io.zipkin.brave</groupId>\n                <artifactId>brave-bom</artifactId>\n                <version>5.9.1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <configuration>\n                    <fork>true</fork>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-40/lab-40-demo/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/Application.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-demo/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/Application2.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo;\n\nimport org.springframework.boot.SpringApplication;\n\n//@SpringBootApplication\npublic class Application2 {\n\n    public static void main(String[] args) {\n        System.setProperty(\"spring.application.name\", \"demo-application-02\");\n        System.setProperty(\"server.port\", \"8079\");\n        SpringApplication.run(Application2.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-demo/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\n@Import(SpanCustomizingAsyncHandlerInterceptor.class)\npublic class SpringMvcConfiguration implements WebMvcConfigurer {\n\n    @Autowired\n    public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;\n\n    /**\n     * Decorates server spans with application-defined web tags\n     */\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        registry.addInterceptor(webMvcTracingCustomizer);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-demo/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.CurrentSpanCustomizer;\nimport brave.SpanCustomizer;\nimport brave.Tracing;\nimport brave.http.HttpTracing;\nimport brave.httpclient.TracingHttpClientBuilder;\nimport brave.servlet.TracingFilter;\nimport org.apache.http.impl.client.CloseableHttpClient;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.web.client.RestTemplateCustomizer;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.http.client.HttpComponentsClientHttpRequestFactory;\nimport org.springframework.web.client.RestTemplate;\nimport zipkin2.Span;\nimport zipkin2.reporter.AsyncReporter;\nimport zipkin2.reporter.Sender;\nimport zipkin2.reporter.okhttp3.OkHttpSender;\n\nimport javax.servlet.Filter;\n\n@Configuration\npublic class ZipkinConfiguration {\n\n    // ==================== 通用配置 ====================\n\n    /**\n     * Configuration for how to send spans to Zipkin\n     */\n    @Bean\n    public Sender sender() {\n        return OkHttpSender.create(\"http://127.0.0.1:9411/api/v2/spans\");\n    }\n\n    /**\n     * Configuration for how to buffer spans into messages for Zipkin\n     */\n    @Bean\n    public AsyncReporter<Span> spanReporter() {\n        return AsyncReporter.create(sender());\n    }\n\n    /**\n     * Controls aspects of tracing such as the service name that shows up in the UI\n     */\n    @Bean\n    public Tracing tracing(@Value(\"${spring.application.name}\") String serviceName) {\n        return Tracing.newBuilder()\n                .localServiceName(serviceName)\n//                .currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder()\n//                        .addScopeDecorator(MDCScopeDecorator.create()) // puts trace IDs into logs\n//                        .build()\n//                )\n                .spanReporter(spanReporter()).build();\n    }\n\n    /**\n     * Allows someone to add tags to a span if a trace is in progress\n     */\n    @Bean\n    public SpanCustomizer spanCustomizer(Tracing tracing) {\n        return CurrentSpanCustomizer.create(tracing);\n    }\n\n    // ==================== HTTP 相关 ====================\n\n    /**\n     * Decides how to name and tag spans. By default they are named the same as the http method\n     */\n    @Bean\n    public HttpTracing httpTracing(Tracing tracing) {\n        return HttpTracing.create(tracing);\n    }\n\n    /**\n     * Creates server spans for http requests\n     */\n    @Bean\n    public Filter tracingFilter(HttpTracing httpTracing) {\n        return TracingFilter.create(httpTracing);\n    }\n\n    // ==================== SpringMVC 相关 ====================\n    // @see SpringMvcConfiguration 类上的，@Import(SpanCustomizingAsyncHandlerInterceptor.class)\n\n    // ==================== HttpClient 相关 ====================\n\n    @Bean\n    public RestTemplateCustomizer useTracedHttpClient(HttpTracing httpTracing) {\n        // 创建 CloseableHttpClient 对象，内置 HttpTracing 进行 HTTP 链路追踪。\n        final CloseableHttpClient httpClient = TracingHttpClientBuilder.create(httpTracing).build();\n        // 创建 RestTemplateCustomizer 对象\n        return new RestTemplateCustomizer() {\n            @Override\n            public void customize(RestTemplate restTemplate) {\n                restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));\n            }\n        };\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-demo/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.web.client.RestTemplateBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private final RestTemplate restTemplate;\n\n    DemoController(@Autowired RestTemplateBuilder restTemplateBuilder) {\n        this.restTemplate = restTemplateBuilder.build();\n    }\n\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return \"echo\";\n    }\n\n    @GetMapping(\"/http\")\n    public String http() {\n//        restTemplate.getForObject(\"https://www.baidu.com\", String.class);\n        restTemplate.getForObject(\"http://127.0.0.1:8079/demo/echo\", String.class);\n        return \"echo\";\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-demo/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application\n"
  },
  {
    "path": "lab-40/lab-40-elasticsearch/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-40-elasticsearch</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 自动化配置 Spring Data Elasticsearch -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>\n        </dependency>\n\n        <!-- Brave 核心库 -->\n        <!-- The below are needed to report traces to http://localhost:9411/api/v2/spans -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.zipkin.reporter2</groupId>\n            <artifactId>zipkin-sender-okhttp3</artifactId>\n        </dependency>\n\n        <!-- Adds the MVC class and method names to server spans -->\n        <!-- Brave 对 Spring MVC 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-spring-webmvc</artifactId>\n        </dependency>\n\n        <!--  Brave 对 Opentracing 的实现 -->\n        <dependency>\n            <groupId>io.opentracing.brave</groupId>\n            <artifactId>brave-opentracing</artifactId>\n            <version>0.35.0</version>\n        </dependency>\n\n        <!-- Opentracing 对 Elasticsearch 的支持 -->\n        <dependency>\n            <groupId>io.opentracing.contrib</groupId>\n            <artifactId>opentracing-elasticsearch6-client</artifactId>\n            <version>0.1.6</version>\n        </dependency>\n\n    </dependencies>\n\n    <dependencyManagement>\n        <!-- Brave Bom 文件 -->\n        <dependencies>\n            <dependency>\n                <groupId>io.zipkin.brave</groupId>\n                <artifactId>brave-bom</artifactId>\n                <version>5.9.1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n</project>\n"
  },
  {
    "path": "lab-40/lab-40-elasticsearch/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/ElasticsearchApplication.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ElasticsearchApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ElasticsearchApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-elasticsearch/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\n@Import(SpanCustomizingAsyncHandlerInterceptor.class)\npublic class SpringMvcConfiguration implements WebMvcConfigurer {\n\n    @Autowired\n    public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;\n\n    /**\n     * Decorates server spans with application-defined web tags\n     */\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        registry.addInterceptor(webMvcTracingCustomizer);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-elasticsearch/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.CurrentSpanCustomizer;\nimport brave.SpanCustomizer;\nimport brave.Tracing;\nimport brave.http.HttpTracing;\nimport brave.opentracing.BraveTracer;\nimport brave.servlet.TracingFilter;\nimport cn.iocoder.springboot.lab40.zipkindemo.spring.TracingTransportClientFactoryBean;\nimport io.opentracing.Tracer;\nimport org.elasticsearch.client.transport.TransportClient;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport zipkin2.Span;\nimport zipkin2.reporter.AsyncReporter;\nimport zipkin2.reporter.Sender;\nimport zipkin2.reporter.okhttp3.OkHttpSender;\n\nimport javax.servlet.Filter;\nimport java.util.Properties;\n\n@Configuration\npublic class ZipkinConfiguration {\n\n    // ==================== 通用配置 ====================\n\n    /**\n     * Configuration for how to send spans to Zipkin\n     */\n    @Bean\n    public Sender sender() {\n        return OkHttpSender.create(\"http://127.0.0.1:9411/api/v2/spans\");\n    }\n\n    /**\n     * Configuration for how to buffer spans into messages for Zipkin\n     */\n    @Bean\n    public AsyncReporter<Span> spanReporter() {\n        return AsyncReporter.create(sender());\n    }\n\n    /**\n     * Controls aspects of tracing such as the service name that shows up in the UI\n     */\n    @Bean\n    public Tracing tracing(@Value(\"${spring.application.name}\") String serviceName) {\n        return Tracing.newBuilder()\n                .localServiceName(serviceName)\n                .spanReporter(spanReporter()).build();\n    }\n\n    @Bean\n    public Tracer openTracer(Tracing tracing) {\n        return BraveTracer.create(tracing);\n    }\n\n    /**\n     * Allows someone to add tags to a span if a trace is in progress\n     */\n    @Bean\n    public SpanCustomizer spanCustomizer(Tracing tracing) {\n        return CurrentSpanCustomizer.create(tracing);\n    }\n\n    // ==================== HTTP 相关 ====================\n\n    /**\n     * Decides how to name and tag spans. By default they are named the same as the http method\n     */\n    @Bean\n    public HttpTracing httpTracing(Tracing tracing) {\n        return HttpTracing.create(tracing);\n    }\n\n    /**\n     * Creates server spans for http requests\n     */\n    @Bean\n    public Filter tracingFilter(HttpTracing httpTracing) {\n        return TracingFilter.create(httpTracing);\n    }\n\n    // ==================== SpringMVC 相关 ====================\n    // @see SpringMvcConfiguration 类上的，@Import(SpanCustomizingAsyncHandlerInterceptor.class)\n\n    // ==================== Elasticsearch 相关 ====================\n\n    @Bean\n    public TransportClient elasticsearchClient(Tracer tracer, ElasticsearchProperties elasticsearchProperties) throws Exception {\n        // 创建 TracingTransportClientFactoryBean 对象\n        TracingTransportClientFactoryBean factory = new TracingTransportClientFactoryBean(tracer);\n        // 设置其属性\n        factory.setClusterNodes(elasticsearchProperties.getClusterNodes());\n        factory.setProperties(this.createElasticsearch(elasticsearchProperties));\n        // 创建 TransportClient 对象，并返回\n        factory.afterPropertiesSet();\n        return factory.getObject();\n    }\n\n    private Properties createElasticsearch(ElasticsearchProperties elasticsearchProperties) {\n        Properties properties = new Properties();\n        properties.put(\"cluster.name\", elasticsearchProperties.getClusterName());\n        properties.putAll(elasticsearchProperties.getProperties());\n        return properties;\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-elasticsearch/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.controller;\n\nimport cn.iocoder.springboot.lab40.zipkindemo.dataobject.ESUserDO;\nimport cn.iocoder.springboot.lab40.zipkindemo.repository.ESUserRepository;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\n    public class DemoController {\n\n    @Autowired\n    private ESUserRepository userRepository;\n\n    @GetMapping(\"/elasticsearch\")\n    public String mysql() {\n        this.findById(1);\n        return \"elasticsearch\";\n    }\n\n    public ESUserDO findById(Integer id) {\n        return userRepository.findById(id).orElse(null);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-elasticsearch/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/dataobject/ESUserDO.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.dataobject;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.elasticsearch.annotations.Document;\n\nimport java.util.Date;\n\n@Document(indexName = \"user\", // 索引名\n        type = \"user\", // 类型。未来的版本即将废弃\n        shards = 1, // 默认索引分区数\n        replicas = 0, // 每个分区的备份数\n        refreshInterval = \"-1\" // 刷新间隔\n)\npublic class ESUserDO {\n\n    @Id\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                \", createTime=\" + createTime +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-elasticsearch/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/repository/ESUserRepository.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.repository;\n\nimport cn.iocoder.springboot.lab40.zipkindemo.dataobject.ESUserDO;\nimport org.springframework.data.elasticsearch.repository.ElasticsearchRepository;\n\npublic interface ESUserRepository extends ElasticsearchRepository<ESUserDO, Integer> {\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-elasticsearch/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/spring/ClusterNodes.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.spring;\n\nimport org.elasticsearch.common.transport.TransportAddress;\nimport org.springframework.data.util.Streamable;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\nimport java.net.InetAddress;\nimport java.net.UnknownHostException;\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nclass ClusterNodes implements Streamable<TransportAddress> {\n\n    public static ClusterNodes DEFAULT = ClusterNodes.of(\"127.0.0.1:9300\");\n\n    private static final String COLON = \":\";\n    private static final String COMMA = \",\";\n\n    private final List<TransportAddress> clusterNodes;\n\n    /**\n     * Creates a new {@link ClusterNodes} by parsing the given source.\n     *\n     * @param source must not be {@literal null} or empty.\n     */\n    private ClusterNodes(String source) {\n\n        Assert.hasText(source, \"Cluster nodes source must not be null or empty!\");\n\n        String[] nodes = StringUtils.delimitedListToStringArray(source, COMMA);\n\n        this.clusterNodes = Arrays.stream(nodes).map(node -> {\n\n            String[] segments = StringUtils.delimitedListToStringArray(node, COLON);\n\n            Assert.isTrue(segments.length == 2,\n                    () -> String.format(\"Invalid cluster node %s in %s! Must be in the format host:port!\", node, source));\n\n            String host = segments[0].trim();\n            String port = segments[1].trim();\n\n            Assert.hasText(host, () -> String.format(\"No host name given cluster node %s!\", node));\n            Assert.hasText(port, () -> String.format(\"No port given in cluster node %s!\", node));\n\n            return new TransportAddress(toInetAddress(host), Integer.valueOf(port));\n\n        }).collect(Collectors.toList());\n    }\n\n    /**\n     * Creates a new {@link ClusterNodes} by parsing the given source. The expected format is a comma separated list of\n     * host-port-combinations separated by a colon: {@code host:port,host:port,…}.\n     *\n     * @param source must not be {@literal null} or empty.\n     * @return\n     */\n    public static ClusterNodes of(String source) {\n        return new ClusterNodes(source);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see java.lang.Iterable#iterator()\n     */\n    @Override\n    public Iterator<TransportAddress> iterator() {\n        return clusterNodes.iterator();\n    }\n\n    private static InetAddress toInetAddress(String host) {\n\n        try {\n            return InetAddress.getByName(host);\n        } catch (UnknownHostException o_O) {\n            throw new IllegalArgumentException(o_O);\n        }\n    }\n}\n"
  },
  {
    "path": "lab-40/lab-40-elasticsearch/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/spring/TracingTransportClientFactoryBean.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.spring;\n\nimport io.opentracing.Tracer;\nimport io.opentracing.contrib.elasticsearch6.TracingPreBuiltTransportClient;\nimport org.elasticsearch.client.transport.TransportClient;\nimport org.elasticsearch.common.settings.Settings;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.data.elasticsearch.client.TransportClientFactoryBean;\n\nimport java.util.Properties;\n\n/**\n * 参考 {@link TransportClientFactoryBean} 来实现。\n */\npublic class TracingTransportClientFactoryBean implements FactoryBean<TransportClient>, InitializingBean, DisposableBean {\n\n    private static final Logger logger = LoggerFactory.getLogger(TransportClientFactoryBean.class);\n    private ClusterNodes clusterNodes = ClusterNodes.of(\"127.0.0.1:9300\");\n    private String clusterName = \"elasticsearch\";\n    private Boolean clientTransportSniff = true;\n    private Boolean clientIgnoreClusterName = Boolean.FALSE;\n    private String clientPingTimeout = \"5s\";\n    private String clientNodesSamplerInterval = \"5s\";\n    private TransportClient client;\n    private Properties properties;\n\n    private Tracer tracer;\n\n    public TracingTransportClientFactoryBean(Tracer tracer) {\n        this.tracer = tracer;\n    }\n\n    @Override\n    public void destroy() throws Exception {\n        try {\n            logger.info(\"Closing elasticSearch  client\");\n            if (client != null) {\n                client.close();\n            }\n        } catch (final Exception e) {\n            logger.error(\"Error closing ElasticSearch client: \", e);\n        }\n    }\n\n    @Override\n    public TransportClient getObject() throws Exception {\n        return client;\n    }\n\n    @Override\n    public Class<TransportClient> getObjectType() {\n        return TransportClient.class;\n    }\n\n    @Override\n    public boolean isSingleton() {\n        return true;\n    }\n\n    @Override\n    public void afterPropertiesSet() throws Exception {\n        buildClient();\n    }\n\n    protected void buildClient() throws Exception {\n        // 创建可追踪的 TracingPreBuiltTransportClient\n        client = new TracingPreBuiltTransportClient(tracer, settings());\n\n        clusterNodes.stream() //\n                .peek(it -> logger.info(\"Adding transport node : \" + it.toString())) //\n                .forEach(client::addTransportAddress);\n\n        client.connectedNodes();\n    }\n\n    private Settings settings() {\n        if (properties != null) {\n            Settings.Builder builder = Settings.builder();\n\n            properties.forEach((key, value) -> {\n                builder.put(key.toString(), value.toString());\n            });\n\n            return builder.build();\n        }\n        return Settings.builder()\n                .put(\"cluster.name\", clusterName)\n                .put(\"client.transport.sniff\", clientTransportSniff)\n                .put(\"client.transport.ignore_cluster_name\", clientIgnoreClusterName)\n                .put(\"client.transport.ping_timeout\", clientPingTimeout)\n                .put(\"client.transport.nodes_sampler_interval\", clientNodesSamplerInterval)\n                .build();\n    }\n\n    public void setClusterNodes(String clusterNodes) {\n        this.clusterNodes = ClusterNodes.of(clusterNodes);\n    }\n\n    public void setClusterName(String clusterName) {\n        this.clusterName = clusterName;\n    }\n\n    public void setClientTransportSniff(Boolean clientTransportSniff) {\n        this.clientTransportSniff = clientTransportSniff;\n    }\n\n    public String getClientNodesSamplerInterval() {\n        return clientNodesSamplerInterval;\n    }\n\n    public void setClientNodesSamplerInterval(String clientNodesSamplerInterval) {\n        this.clientNodesSamplerInterval = clientNodesSamplerInterval;\n    }\n\n    public String getClientPingTimeout() {\n        return clientPingTimeout;\n    }\n\n    public void setClientPingTimeout(String clientPingTimeout) {\n        this.clientPingTimeout = clientPingTimeout;\n    }\n\n    public Boolean getClientIgnoreClusterName() {\n        return clientIgnoreClusterName;\n    }\n\n    public void setClientIgnoreClusterName(Boolean clientIgnoreClusterName) {\n        this.clientIgnoreClusterName = clientIgnoreClusterName;\n    }\n\n    public void setProperties(Properties properties) {\n        this.properties = properties;\n    }\n}\n"
  },
  {
    "path": "lab-40/lab-40-elasticsearch/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-application-elasticsearch\n\n  data:\n    # Elasticsearch 配置项\n    elasticsearch:\n      cluster-name: elasticsearch # 集群名\n      cluster-nodes: 127.0.0.1:9300 # 集群节点\n"
  },
  {
    "path": "lab-40/lab-40-kafka/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.1.11.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-40-kafka</artifactId>\n\n    <dependencies>\n        <!-- 引入 Spring-Kafka 依赖 -->\n        <!-- 已经内置 kafka-clients 依赖，所以无需重复引入 -->\n        <dependency>\n            <groupId>org.springframework.kafka</groupId>\n            <artifactId>spring-kafka</artifactId>\n            <version>2.2.11.RELEASE</version>\n        </dependency>\n\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- Brave 核心库 -->\n        <!-- The below are needed to report traces to http://localhost:9411/api/v2/spans -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.zipkin.reporter2</groupId>\n            <artifactId>zipkin-sender-okhttp3</artifactId>\n        </dependency>\n\n        <!-- Adds the MVC class and method names to server spans -->\n        <!-- Brave 对 Spring MVC 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-spring-webmvc</artifactId>\n        </dependency>\n        <!-- Brave 对 Kafka 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-kafka-clients</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <dependencyManagement>\n        <!-- Brave Bom 文件 -->\n        <dependencies>\n            <dependency>\n                <groupId>io.zipkin.brave</groupId>\n                <artifactId>brave-bom</artifactId>\n                <version>5.9.1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n</project>\n"
  },
  {
    "path": "lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/KafkaApplication.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class KafkaApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(KafkaApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\n@Import(SpanCustomizingAsyncHandlerInterceptor.class) // 创建拦截器 SpanCustomizingAsyncHandlerInterceptor Bean\npublic class SpringMvcConfiguration implements WebMvcConfigurer {\n\n    @Autowired\n    public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;\n\n    /**\n     * Decorates server spans with application-defined web tags\n     */\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) { // 记录 SpringMVC 相关信息到 Span 中\n        registry.addInterceptor(webMvcTracingCustomizer);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.CurrentSpanCustomizer;\nimport brave.SpanCustomizer;\nimport brave.Tracing;\nimport brave.http.HttpTracing;\nimport brave.kafka.clients.KafkaTracing;\nimport brave.servlet.TracingFilter;\nimport org.apache.kafka.clients.consumer.Consumer;\nimport org.apache.kafka.clients.producer.Producer;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.kafka.KafkaProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.kafka.core.ConsumerFactory;\nimport org.springframework.kafka.core.DefaultKafkaConsumerFactory;\nimport org.springframework.kafka.core.DefaultKafkaProducerFactory;\nimport org.springframework.kafka.core.ProducerFactory;\nimport zipkin2.Span;\nimport zipkin2.reporter.AsyncReporter;\nimport zipkin2.reporter.Sender;\nimport zipkin2.reporter.okhttp3.OkHttpSender;\n\nimport javax.servlet.Filter;\nimport java.util.Properties;\n\n@Configuration\npublic class ZipkinConfiguration {\n\n    // ==================== 通用配置 ====================\n\n    /**\n     * Configuration for how to send spans to Zipkin\n     */\n    @Bean\n    public Sender sender() { // Sender 采用 HTTP 通信方式\n        return OkHttpSender.create(\"http://127.0.0.1:9411/api/v2/spans\");\n    }\n\n    /**\n     * Configuration for how to buffer spans into messages for Zipkin\n     */\n    @Bean\n    public AsyncReporter<Span> spanReporter() { // 异步 Reporter\n        return AsyncReporter.create(sender());\n    }\n\n    /**\n     * Controls aspects of tracing such as the service name that shows up in the UI\n     */\n    @Bean\n    public Tracing tracing(@Value(\"${spring.application.name}\") String serviceName) {\n        return Tracing.newBuilder()\n                .localServiceName(serviceName) // 应用名\n                .spanReporter(this.spanReporter()).build();\n    }\n\n    /**\n     * Allows someone to add tags to a span if a trace is in progress\n     */\n    @Bean\n    public SpanCustomizer spanCustomizer(Tracing tracing) {\n        return CurrentSpanCustomizer.create(tracing);\n    }\n\n    // ==================== HTTP 相关 ====================\n\n    /**\n     * Decides how to name and tag spans. By default they are named the same as the http method\n     */\n    @Bean\n    public HttpTracing httpTracing(Tracing tracing) {\n        return HttpTracing.create(tracing);\n    }\n\n    /**\n     * Creates server spans for http requests\n     */\n    @Bean\n    public Filter tracingFilter(HttpTracing httpTracing) { // 拦截请求，记录 HTTP 请求的链路信息\n        return TracingFilter.create(httpTracing);\n    }\n\n    // ==================== SpringMVC 相关 ====================\n    // @see SpringMvcConfiguration 类上的，@Import(SpanCustomizingAsyncHandlerInterceptor.class) 。因为 SpanCustomizingAsyncHandlerInterceptor 未提供 public 构造方法\n\n    // ==================== Kafka 相关 ====================\n\n    @Bean\n    public KafkaTracing kafkaTracing(Tracing tracing) {\n        return KafkaTracing.newBuilder(tracing)\n                .remoteServiceName(\"demo-mq-kafka\") // 远程 Kafka 服务名，可自定义\n                .build();\n    }\n\n    @Bean\n    public ProducerFactory<?, ?> kafkaProducerFactory(KafkaProperties properties, KafkaTracing kafkaTracing) {\n        // 创建 DefaultKafkaProducerFactory 对象\n        DefaultKafkaProducerFactory<?, ?> factory = new DefaultKafkaProducerFactory(properties.buildProducerProperties()) {\n\n            @Override\n            public Producer createProducer() {\n                // 创建默认的 Producer\n                Producer<?, ?> producer = super.createProducer();\n                // 创建可链路追踪的 Producer\n                return kafkaTracing.producer(producer);\n            }\n\n        };\n\n        // 设置事务前缀\n        String transactionIdPrefix = properties.getProducer().getTransactionIdPrefix();\n        if (transactionIdPrefix != null) {\n            factory.setTransactionIdPrefix(transactionIdPrefix);\n        }\n\n        return factory;\n    }\n\n    @Bean\n    public ConsumerFactory<?, ?> kafkaConsumerFactory(KafkaProperties properties, KafkaTracing kafkaTracing) {\n        // 创建 DefaultKafkaConsumerFactory 对象\n        return new DefaultKafkaConsumerFactory(properties.buildConsumerProperties()) {\n\n            @Override\n            public Consumer<?, ?> createConsumer(String groupId, String clientIdPrefix,  String clientIdSuffix) {\n                return this.createConsumer(groupId, clientIdPrefix, clientIdSuffix, null);\n            }\n\n            @Override\n            public Consumer<?, ?> createConsumer(String groupId, String clientIdPrefix, final String clientIdSuffixArg, Properties properties) {\n                // 创建默认的 Consumer\n                Consumer<?, ?> consumer = super.createConsumer(groupId, clientIdPrefix, clientIdSuffixArg, properties);\n                // 创建可链路追踪的 Consumer\n                return kafkaTracing.consumer(consumer);\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/consumer/DemoConsumer.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.consumer;\n\nimport cn.iocoder.springboot.lab40.zipkindemo.message.DemoMessage;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.kafka.annotation.KafkaListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class DemoConsumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @KafkaListener(topics = DemoMessage.TOPIC,\n            groupId = \"demo-consumer-group-\" + DemoMessage.TOPIC)\n    public void onMessage(DemoMessage message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.controller;\n\nimport cn.iocoder.springboot.lab40.zipkindemo.producer.DemoProducer;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.concurrent.ExecutionException;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private DemoProducer producer;\n\n    @GetMapping(\"/kafka\")\n    public String echo() throws ExecutionException, InterruptedException {\n        this.sendMessage(1);\n        return \"kafka\";\n    }\n\n    public void sendMessage(Integer id) throws ExecutionException, InterruptedException {\n        producer.syncSend(id);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/message/DemoMessage.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.message;\n\n/**\n * 示例 Message 消息\n */\npublic class DemoMessage {\n\n    public static final String TOPIC = \"DEMO\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public DemoMessage setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"DemoMessage{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-kafka/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/producer/DemoProducer.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.producer;\n\nimport cn.iocoder.springboot.lab40.zipkindemo.message.DemoMessage;\nimport org.springframework.kafka.core.KafkaTemplate;\nimport org.springframework.kafka.support.SendResult;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.concurrent.ExecutionException;\n\n@Component\npublic class DemoProducer {\n\n    @Resource\n    private KafkaTemplate<Object, Object> kafkaTemplate;\n\n    public SendResult syncSend(Integer id) throws ExecutionException, InterruptedException {\n        // 创建 DemoMessage 消息\n        DemoMessage message = new DemoMessage();\n        message.setId(id);\n        // 同步发送消息\n        return kafkaTemplate.send(DemoMessage.TOPIC, message).get();\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-kafka/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application-kafka\n\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n    # Kafka Producer 配置项\n    producer:\n      acks: 1 # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。\n      retries: 3 # 发送失败时，重试发送的次数\n      key-serializer: org.apache.kafka.common.serialization.StringSerializer # 消息的 key 的序列化\n      value-serializer: org.springframework.kafka.support.serializer.JsonSerializer # 消息的 value 的序列化\n    # Kafka Consumer 配置项\n    consumer:\n      auto-offset-reset: earliest # 设置消费者分组最初的消费进度为 earliest 。可参考博客 https://blog.csdn.net/lishuangzhe7047/article/details/74530417 理解\n      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer\n      value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer\n      properties:\n        spring:\n          json:\n            trusted:\n              packages: cn.iocoder.springboot.lab40.zipkindemo.message # 消息 POJO 可信目录，解决 JSON 无法反序列化的问题\n    # Kafka Consumer Listener 监听器配置\n    listener:\n      missing-topics-fatal: false # 消费监听接口监听的主题不存在时，默认会报错。所以通过设置为 false ，解决报错\n"
  },
  {
    "path": "lab-40/lab-40-logback/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-40-logback</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- Brave 核心库 -->\n        <!-- The below are needed to report traces to http://localhost:9411/api/v2/spans -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.zipkin.reporter2</groupId>\n            <artifactId>zipkin-sender-okhttp3</artifactId>\n        </dependency>\n\n        <!-- Adds the MVC class and method names to server spans -->\n        <!-- Brave 对 Spring MVC 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-spring-webmvc</artifactId>\n        </dependency>\n        <!-- Integrates so you can use log patterns like %X{traceId}/%X{spanId} -->\n        <!-- Brave 对 SLF4J 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-context-slf4j</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <dependencyManagement>\n        <!-- Brave Bom 文件 -->\n        <dependencies>\n            <dependency>\n                <groupId>io.zipkin.brave</groupId>\n                <artifactId>brave-bom</artifactId>\n                <version>5.9.1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n</project>\n"
  },
  {
    "path": "lab-40/lab-40-logback/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/LogbackApplication.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class LogbackApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(LogbackApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-logback/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\n@Import(SpanCustomizingAsyncHandlerInterceptor.class)\npublic class SpringMvcConfiguration implements WebMvcConfigurer {\n\n    @Autowired\n    public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;\n\n    /**\n     * Decorates server spans with application-defined web tags\n     */\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        registry.addInterceptor(webMvcTracingCustomizer);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-logback/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.CurrentSpanCustomizer;\nimport brave.SpanCustomizer;\nimport brave.Tracing;\nimport brave.context.slf4j.MDCScopeDecorator;\nimport brave.http.HttpTracing;\nimport brave.propagation.ThreadLocalCurrentTraceContext;\nimport brave.servlet.TracingFilter;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport zipkin2.Span;\nimport zipkin2.reporter.AsyncReporter;\nimport zipkin2.reporter.Sender;\nimport zipkin2.reporter.okhttp3.OkHttpSender;\n\nimport javax.servlet.Filter;\n\n@Configuration\npublic class ZipkinConfiguration {\n\n    // ==================== 通用配置 ====================\n\n    /**\n     * Configuration for how to send spans to Zipkin\n     */\n    @Bean\n    public Sender sender() {\n        return OkHttpSender.create(\"http://127.0.0.1:9411/api/v2/spans\");\n    }\n\n    /**\n     * Configuration for how to buffer spans into messages for Zipkin\n     */\n    @Bean\n    public AsyncReporter<Span> spanReporter() {\n        return AsyncReporter.create(sender());\n    }\n\n    /**\n     * Controls aspects of tracing such as the service name that shows up in the UI\n     */\n    @Bean\n    public Tracing tracing(@Value(\"${spring.application.name}\") String serviceName) {\n        return Tracing.newBuilder()\n                .localServiceName(serviceName)\n                .currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder()\n                        .addScopeDecorator(MDCScopeDecorator.create()) // puts trace IDs into logs\n                        .build()\n                )\n                .spanReporter(spanReporter()).build();\n    }\n\n    /**\n     * Allows someone to add tags to a span if a trace is in progress\n     */\n    @Bean\n    public SpanCustomizer spanCustomizer(Tracing tracing) {\n        return CurrentSpanCustomizer.create(tracing);\n    }\n\n    // ==================== HTTP 相关 ====================\n\n    /**\n     * Decides how to name and tag spans. By default they are named the same as the http method\n     */\n    @Bean\n    public HttpTracing httpTracing(Tracing tracing) {\n        return HttpTracing.create(tracing);\n    }\n\n    /**\n     * Creates server spans for http requests\n     */\n    @Bean\n    public Filter tracingFilter(HttpTracing httpTracing) {\n        return TracingFilter.create(httpTracing);\n    }\n\n    // ==================== SpringMVC 相关 ====================\n    // @see SpringMvcConfiguration 类上的，@Import(SpanCustomizingAsyncHandlerInterceptor.class)\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-logback/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/logback\")\n    public String echo() {\n        logger.info(\"测试日志\");\n        return \"logback\";\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-logback/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application-springmvc\n\nlogging:\n  pattern:\n    console: \"%clr(%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %X{traceId}/%X{spanId} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}\"\n    file: \"%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } %X{traceId}/%X{spanId} --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}\"\n"
  },
  {
    "path": "lab-40/lab-40-mongodb/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-40-mongodb</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 自动化配置 Spring Data Mongodb -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-mongodb</artifactId>\n        </dependency>\n\n        <!-- Brave 核心库 -->\n        <!-- The below are needed to report traces to http://localhost:9411/api/v2/spans -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.zipkin.reporter2</groupId>\n            <artifactId>zipkin-sender-okhttp3</artifactId>\n        </dependency>\n\n        <!-- Adds the MVC class and method names to server spans -->\n        <!-- Brave 对 Spring MVC 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-spring-webmvc</artifactId>\n        </dependency>\n\n        <!--  Brave 对 Opentracing 的实现 -->\n        <dependency>\n            <groupId>io.opentracing.brave</groupId>\n            <artifactId>brave-opentracing</artifactId>\n            <version>0.35.0</version>\n        </dependency>\n\n        <!-- Opentracing 对 MongoDB 的支持 -->\n        <dependency>\n            <groupId>io.opentracing.contrib</groupId>\n            <artifactId>opentracing-mongo-driver</artifactId>\n            <version>0.1.5</version>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <!-- Brave Bom 文件 -->\n        <dependencies>\n            <dependency>\n                <groupId>io.zipkin.brave</groupId>\n                <artifactId>brave-bom</artifactId>\n                <version>5.9.1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n</project>\n"
  },
  {
    "path": "lab-40/lab-40-mongodb/src/main/java/cn/iocoder/springboot/lab40/zipkin/MongoDBApplication.java",
    "content": "package cn.iocoder.springboot.lab40.zipkin;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class MongoDBApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(MongoDBApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-mongodb/src/main/java/cn/iocoder/springboot/lab40/zipkin/config/SpringMvcConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkin.config;\n\nimport brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\n@Import(SpanCustomizingAsyncHandlerInterceptor.class)\npublic class SpringMvcConfiguration implements WebMvcConfigurer {\n\n    @Autowired\n    public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;\n\n    /**\n     * Decorates server spans with application-defined web tags\n     */\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        registry.addInterceptor(webMvcTracingCustomizer);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-mongodb/src/main/java/cn/iocoder/springboot/lab40/zipkin/config/ZipkinConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkin.config;\n\nimport brave.CurrentSpanCustomizer;\nimport brave.SpanCustomizer;\nimport brave.Tracing;\nimport brave.http.HttpTracing;\nimport brave.opentracing.BraveTracer;\nimport brave.servlet.TracingFilter;\nimport com.mongodb.MongoClientOptions;\nimport io.opentracing.Tracer;\nimport io.opentracing.contrib.mongo.common.TracingCommandListener;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport zipkin2.Span;\nimport zipkin2.reporter.AsyncReporter;\nimport zipkin2.reporter.Sender;\nimport zipkin2.reporter.okhttp3.OkHttpSender;\n\nimport javax.servlet.Filter;\n\n@Configuration\npublic class ZipkinConfiguration {\n\n    // ==================== 通用配置 ====================\n\n    /**\n     * Configuration for how to send spans to Zipkin\n     */\n    @Bean\n    public Sender sender() {\n        return OkHttpSender.create(\"http://127.0.0.1:9411/api/v2/spans\");\n    }\n\n    /**\n     * Configuration for how to buffer spans into messages for Zipkin\n     */\n    @Bean\n    public AsyncReporter<Span> spanReporter() {\n        return AsyncReporter.create(sender());\n    }\n\n    /**\n     * Controls aspects of tracing such as the service name that shows up in the UI\n     */\n    @Bean\n    public Tracing tracing(@Value(\"${spring.application.name}\") String serviceName) {\n        return Tracing.newBuilder()\n                .localServiceName(serviceName)\n                .spanReporter(spanReporter()).build();\n    }\n\n    @Bean\n    public Tracer openTracer(Tracing tracing) {\n        return BraveTracer.create(tracing);\n    }\n\n    /**\n     * Allows someone to add tags to a span if a trace is in progress\n     */\n    @Bean\n    public SpanCustomizer spanCustomizer(Tracing tracing) {\n        return CurrentSpanCustomizer.create(tracing);\n    }\n\n    // ==================== HTTP 相关 ====================\n\n    /**\n     * Decides how to name and tag spans. By default they are named the same as the http method\n     */\n    @Bean\n    public HttpTracing httpTracing(Tracing tracing) {\n        return HttpTracing.create(tracing);\n    }\n\n    /**\n     * Creates server spans for http requests\n     */\n    @Bean\n    public Filter tracingFilter(HttpTracing httpTracing) {\n        return TracingFilter.create(httpTracing);\n    }\n\n    // ==================== SpringMVC 相关 ====================\n    // @see SpringMvcConfiguration 类上的，@Import(SpanCustomizingAsyncHandlerInterceptor.class)\n\n    // ==================== MongoDB 相关 ====================\n\n    @Bean\n    public MongoClientOptions mongoClientOptions(Tracer tracer) {\n        // 创建 TracingCommandListener 对象\n        TracingCommandListener listener = new TracingCommandListener.Builder(tracer).build();\n        // 创建 MongoClientOptions 对象，并设置监听器\n        return MongoClientOptions.builder().addCommandListener(listener).build();\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-mongodb/src/main/java/cn/iocoder/springboot/lab40/zipkin/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab40.zipkin.controller;\n\nimport cn.iocoder.springboot.lab40.zipkin.dataobject.UserDO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.mongodb.core.MongoTemplate;\nimport org.springframework.data.mongodb.core.query.Criteria;\nimport org.springframework.data.mongodb.core.query.Query;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private MongoTemplate mongoTemplate;\n\n    @GetMapping(\"/mongodb\")\n    public String mysql() {\n        this.findById(1);\n        return \"mongodb\";\n    }\n\n    public UserDO findById(Integer id) {\n        return mongoTemplate.findOne(new Query(Criteria.where(\"_id\").is(id)), UserDO.class);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-mongodb/src/main/java/cn/iocoder/springboot/lab40/zipkin/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab40.zipkin.dataobject;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.mongodb.core.mapping.Document;\n\nimport java.util.Date;\n\n/**\n * 用户 DO\n */\n@Document(collection = \"User\")\npublic class UserDO {\n\n    @Id\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                \", createTime=\" + createTime +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-mongodb/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: dmeo-application-mongodb\n\n  data:\n    # MongoDB 配置项，对应 MongoProperties 类\n    mongodb:\n      host: 127.0.0.1\n      port: 27017\n      database: yourdatabase\n      username: test01\n      password: password01\n      # 上述属性，也可以只配置 uri\n"
  },
  {
    "path": "lab-40/lab-40-mysql/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-40-mysql</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.46</version>\n        </dependency>\n\n        <!-- Brave 核心库 -->\n        <!-- The below are needed to report traces to http://localhost:9411/api/v2/spans -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.zipkin.reporter2</groupId>\n            <artifactId>zipkin-sender-okhttp3</artifactId>\n        </dependency>\n\n        <!-- Adds the MVC class and method names to server spans -->\n        <!-- Brave 对 Spring MVC 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-spring-webmvc</artifactId>\n        </dependency>\n\n        <!-- Brave 对 MySQL 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-mysql</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <dependencyManagement>\n        <!-- Brave Bom 文件 -->\n        <dependencies>\n            <dependency>\n                <groupId>io.zipkin.brave</groupId>\n                <artifactId>brave-bom</artifactId>\n                <version>5.9.1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n</project>\n"
  },
  {
    "path": "lab-40/lab-40-mysql/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/MySQLApplication.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class MySQLApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(MySQLApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-mysql/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\n@Import(SpanCustomizingAsyncHandlerInterceptor.class)\npublic class SpringMvcConfiguration implements WebMvcConfigurer {\n\n    @Autowired\n    public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;\n\n    /**\n     * Decorates server spans with application-defined web tags\n     */\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        registry.addInterceptor(webMvcTracingCustomizer);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-mysql/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.CurrentSpanCustomizer;\nimport brave.SpanCustomizer;\nimport brave.Tracing;\nimport brave.http.HttpTracing;\nimport brave.servlet.TracingFilter;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport zipkin2.Span;\nimport zipkin2.reporter.AsyncReporter;\nimport zipkin2.reporter.Sender;\nimport zipkin2.reporter.okhttp3.OkHttpSender;\n\nimport javax.servlet.Filter;\n\n@Configuration\npublic class ZipkinConfiguration {\n\n    // ==================== 通用配置 ====================\n\n    /**\n     * Configuration for how to send spans to Zipkin\n     */\n    @Bean\n    public Sender sender() {\n        return OkHttpSender.create(\"http://127.0.0.1:9411/api/v2/spans\");\n    }\n\n    /**\n     * Configuration for how to buffer spans into messages for Zipkin\n     */\n    @Bean\n    public AsyncReporter<Span> spanReporter() {\n        return AsyncReporter.create(sender());\n    }\n\n    /**\n     * Controls aspects of tracing such as the service name that shows up in the UI\n     */\n    @Bean\n    public Tracing tracing(@Value(\"${spring.application.name}\") String serviceName) {\n        return Tracing.newBuilder()\n                .localServiceName(serviceName)\n                .spanReporter(spanReporter()).build();\n    }\n\n    /**\n     * Allows someone to add tags to a span if a trace is in progress\n     */\n    @Bean\n    public SpanCustomizer spanCustomizer(Tracing tracing) {\n        return CurrentSpanCustomizer.create(tracing);\n    }\n\n    // ==================== HTTP 相关 ====================\n\n    /**\n     * Decides how to name and tag spans. By default they are named the same as the http method\n     */\n    @Bean\n    public HttpTracing httpTracing(Tracing tracing) {\n        return HttpTracing.create(tracing);\n    }\n\n    /**\n     * Creates server spans for http requests\n     */\n    @Bean\n    public Filter tracingFilter(HttpTracing httpTracing) {\n        return TracingFilter.create(httpTracing);\n    }\n\n    // ==================== SpringMVC 相关 ====================\n    // @see SpringMvcConfiguration 类上的，@Import(SpanCustomizingAsyncHandlerInterceptor.class)\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-mysql/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.BeanPropertyRowMapper;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private JdbcTemplate template;\n\n    @GetMapping(\"/mysql\")\n    public String echo() {\n        this.selectById(1);\n        return \"mysql\";\n    }\n\n    public Object selectById(Integer id) {\n        return template.queryForObject(\"SELECT id, username, password FROM t_user WHERE id = ?\",\n                new BeanPropertyRowMapper<>(Object.class), // 结果转换成对应的对象。Object 理论来说是 UserDO.class ，这里偷懒了。\n                id);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-mysql/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application-mysql\n\n  # datasource 数据源配置内容\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/lab-39-mysql?useSSL=false&useUnicode=true&characterEncoding=UTF-8&statementInterceptors=brave.mysql.TracingStatementInterceptor&zipkinServiceName=demo-db-mysql\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n"
  },
  {
    "path": "lab-40/lab-40-opentracing/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-40-opentracing</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- Brave 核心库 -->\n        <!-- The below are needed to report traces to http://localhost:9411/api/v2/spans -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.zipkin.reporter2</groupId>\n            <artifactId>zipkin-sender-okhttp3</artifactId>\n        </dependency>\n\n        <!-- Adds the MVC class and method names to server spans -->\n        <!-- Brave 对 Spring MVC 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-spring-webmvc</artifactId>\n        </dependency>\n\n        <!--  Brave 对 Opentracing 的实现 -->\n        <dependency>\n            <groupId>io.opentracing.brave</groupId>\n            <artifactId>brave-opentracing</artifactId>\n            <version>0.35.0</version>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <!-- Brave Bom 文件 -->\n        <dependencies>\n            <dependency>\n                <groupId>io.zipkin.brave</groupId>\n                <artifactId>brave-bom</artifactId>\n                <version>5.9.1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n</project>\n"
  },
  {
    "path": "lab-40/lab-40-opentracing/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/OpentracingApplication.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class OpentracingApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(OpentracingApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-opentracing/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\n@Import(SpanCustomizingAsyncHandlerInterceptor.class)\npublic class SpringMvcConfiguration implements WebMvcConfigurer {\n\n    @Autowired\n    public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;\n\n    /**\n     * Decorates server spans with application-defined web tags\n     */\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        registry.addInterceptor(webMvcTracingCustomizer);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-opentracing/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.CurrentSpanCustomizer;\nimport brave.SpanCustomizer;\nimport brave.Tracing;\nimport brave.http.HttpTracing;\nimport brave.opentracing.BraveTracer;\nimport brave.servlet.TracingFilter;\nimport io.opentracing.Tracer;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport zipkin2.Span;\nimport zipkin2.reporter.AsyncReporter;\nimport zipkin2.reporter.Sender;\nimport zipkin2.reporter.okhttp3.OkHttpSender;\n\nimport javax.servlet.Filter;\n\n@Configuration\npublic class ZipkinConfiguration {\n\n    // ==================== 通用配置 ====================\n\n    /**\n     * Configuration for how to send spans to Zipkin\n     */\n    @Bean\n    public Sender sender() {\n        return OkHttpSender.create(\"http://127.0.0.1:9411/api/v2/spans\");\n    }\n\n    /**\n     * Configuration for how to buffer spans into messages for Zipkin\n     */\n    @Bean\n    public AsyncReporter<Span> spanReporter() {\n        return AsyncReporter.create(sender());\n    }\n\n    /**\n     * Controls aspects of tracing such as the service name that shows up in the UI\n     */\n    @Bean\n    public Tracing tracing(@Value(\"${spring.application.name}\") String serviceName) {\n        return Tracing.newBuilder()\n                .localServiceName(serviceName)\n                .spanReporter(spanReporter()).build();\n    }\n\n    @Bean\n    public Tracer openTracer(Tracing tracing) {\n        return BraveTracer.create(tracing);\n    }\n\n    /**\n     * Allows someone to add tags to a span if a trace is in progress\n     */\n    @Bean\n    public SpanCustomizer spanCustomizer(Tracing tracing) {\n        return CurrentSpanCustomizer.create(tracing);\n    }\n\n    // ==================== HTTP 相关 ====================\n\n    /**\n     * Decides how to name and tag spans. By default they are named the same as the http method\n     */\n    @Bean\n    public HttpTracing httpTracing(Tracing tracing) {\n        return HttpTracing.create(tracing);\n    }\n\n    /**\n     * Creates server spans for http requests\n     */\n    @Bean\n    public Filter tracingFilter(HttpTracing httpTracing) {\n        return TracingFilter.create(httpTracing);\n    }\n\n    // ==================== SpringMVC 相关 ====================\n    // @see SpringMvcConfiguration 类上的，@Import(SpanCustomizingAsyncHandlerInterceptor.class)\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-opentracing/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.controller;\n\nimport io.opentracing.Tracer;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private Tracer tracer;\n\n    @GetMapping(\"/opentracing\")\n    public String echo() {\n        // 创建一个 Span\n        tracer.buildSpan(\"custom_operation\").withTag(\"mp\", \"芋道源码\").start().finish();\n\n        // 返回\n        return \"opentracing\";\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-opentracing/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-application-opentracing\n"
  },
  {
    "path": "lab-40/lab-40-rabbitmq/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-40-rabbitmq</artifactId>\n\n    <dependencies>\n        <!-- 实现对 RabbitMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-amqp</artifactId>\n        </dependency>\n\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- Brave 核心库 -->\n        <!-- The below are needed to report traces to http://localhost:9411/api/v2/spans -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.zipkin.reporter2</groupId>\n            <artifactId>zipkin-sender-okhttp3</artifactId>\n        </dependency>\n\n        <!-- Adds the MVC class and method names to server spans -->\n        <!-- Brave 对 Spring MVC 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-spring-webmvc</artifactId>\n        </dependency>\n        <!-- Brave 对 RabbitMQ 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-spring-rabbit</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <dependencyManagement>\n        <!-- Brave Bom 文件 -->\n        <dependencies>\n            <dependency>\n                <groupId>io.zipkin.brave</groupId>\n                <artifactId>brave-bom</artifactId>\n                <version>5.9.1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n</project>\n"
  },
  {
    "path": "lab-40/lab-40-rabbitmq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/RabbitMQApplication.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class RabbitMQApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(RabbitMQApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-rabbitmq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/RabbitConfig.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport cn.iocoder.springboot.lab40.zipkindemo.message.DemoMessage;\nimport org.springframework.amqp.core.Binding;\nimport org.springframework.amqp.core.BindingBuilder;\nimport org.springframework.amqp.core.DirectExchange;\nimport org.springframework.amqp.core.Queue;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class RabbitConfig {\n\n    // 创建 Queue\n    @Bean\n    public Queue demoQueue() {\n        return new Queue(DemoMessage.QUEUE, // Queue 名字\n                true, // durable: 是否持久化\n                false, // exclusive: 是否排它\n                false); // autoDelete: 是否自动删除\n    }\n\n    // 创建 Direct Exchange\n    @Bean\n    public DirectExchange demoExchange() {\n        return new DirectExchange(DemoMessage.EXCHANGE,\n                true,  // durable: 是否持久化\n                false);  // exclusive: 是否排它\n    }\n\n    // 创建 Binding\n    // Exchange：DemoMessage.EXCHANGE\n    // Routing key：DemoMessage.ROUTING_KEY\n    // Queue：DemoMessage.QUEUE\n    @Bean\n    public Binding demoBinding() {\n        return BindingBuilder.bind(demoQueue()).to(demoExchange()).with(DemoMessage.ROUTING_KEY);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-rabbitmq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\n@Import(SpanCustomizingAsyncHandlerInterceptor.class) // 创建拦截器 SpanCustomizingAsyncHandlerInterceptor Bean\npublic class SpringMvcConfiguration implements WebMvcConfigurer {\n\n    @Autowired\n    public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;\n\n    /**\n     * Decorates server spans with application-defined web tags\n     */\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) { // 记录 SpringMVC 相关信息到 Span 中\n        registry.addInterceptor(webMvcTracingCustomizer);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-rabbitmq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.CurrentSpanCustomizer;\nimport brave.SpanCustomizer;\nimport brave.Tracing;\nimport brave.http.HttpTracing;\nimport brave.servlet.TracingFilter;\nimport brave.spring.rabbit.SpringRabbitTracing;\nimport org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.beans.factory.config.BeanPostProcessor;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport zipkin2.Span;\nimport zipkin2.reporter.AsyncReporter;\nimport zipkin2.reporter.Sender;\nimport zipkin2.reporter.okhttp3.OkHttpSender;\n\nimport javax.servlet.Filter;\n\n@Configuration\npublic class ZipkinConfiguration {\n\n    // ==================== 通用配置 ====================\n\n    /**\n     * Configuration for how to send spans to Zipkin\n     */\n    @Bean\n    public Sender sender() { // Sender 采用 HTTP 通信方式\n        return OkHttpSender.create(\"http://127.0.0.1:9411/api/v2/spans\");\n    }\n\n    /**\n     * Configuration for how to buffer spans into messages for Zipkin\n     */\n    @Bean\n    public AsyncReporter<Span> spanReporter() { // 异步 Reporter\n        return AsyncReporter.create(sender());\n    }\n\n    /**\n     * Controls aspects of tracing such as the service name that shows up in the UI\n     */\n    @Bean\n    public Tracing tracing(@Value(\"${spring.application.name}\") String serviceName) {\n        return Tracing.newBuilder()\n                .localServiceName(serviceName) // 应用名\n                .spanReporter(this.spanReporter()).build();\n    }\n\n    /**\n     * Allows someone to add tags to a span if a trace is in progress\n     */\n    @Bean\n    public SpanCustomizer spanCustomizer(Tracing tracing) {\n        return CurrentSpanCustomizer.create(tracing);\n    }\n\n    // ==================== HTTP 相关 ====================\n\n    /**\n     * Decides how to name and tag spans. By default they are named the same as the http method\n     */\n    @Bean\n    public HttpTracing httpTracing(Tracing tracing) {\n        return HttpTracing.create(tracing);\n    }\n\n    /**\n     * Creates server spans for http requests\n     */\n    @Bean\n    public Filter tracingFilter(HttpTracing httpTracing) { // 拦截请求，记录 HTTP 请求的链路信息\n        return TracingFilter.create(httpTracing);\n    }\n\n    // ==================== SpringMVC 相关 ====================\n    // @see SpringMvcConfiguration 类上的，@Import(SpanCustomizingAsyncHandlerInterceptor.class) 。因为 SpanCustomizingAsyncHandlerInterceptor 未提供 public 构造方法\n\n    // ==================== RabbitMQ 相关 ====================\n\n    @Bean\n    public SpringRabbitTracing springRabbitTracing(Tracing tracing) {\n        return SpringRabbitTracing.newBuilder(tracing)\n                .remoteServiceName(\"demo-mq-rabbit\") // 远程 RabbitMQ 服务名，可自定义\n                .build();\n    }\n\n    @Bean\n    public BeanPostProcessor rabbitmqBeanPostProcessor(SpringRabbitTracing springRabbitTracing) {\n        return new BeanPostProcessor() {\n\n            @Override\n            public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {\n                return bean;\n            }\n\n            @Override\n            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {\n                // 如果是 RabbitTemplate ，针对 RabbitMQ Producer\n                if (bean instanceof RabbitTemplate) {\n                    return springRabbitTracing.decorateRabbitTemplate((RabbitTemplate) bean);\n                }\n                // 如果是 SimpleRabbitListenerContainerFactory ，针对 RabbitMQ Consumer\n                if (bean instanceof SimpleRabbitListenerContainerFactory) {\n                    return springRabbitTracing.decorateSimpleRabbitListenerContainerFactory((SimpleRabbitListenerContainerFactory) bean);\n                }\n                return bean;\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-rabbitmq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/consumer/DemoConsumer.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.consumer;\n\nimport cn.iocoder.springboot.lab40.zipkindemo.message.DemoMessage;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.rabbit.annotation.RabbitHandler;\nimport org.springframework.amqp.rabbit.annotation.RabbitListener;\nimport org.springframework.stereotype.Component;\n\n@Component\n@RabbitListener(queues = DemoMessage.QUEUE)\npublic class DemoConsumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @RabbitHandler\n    public void onMessage(DemoMessage message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-rabbitmq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.controller;\n\nimport cn.iocoder.springboot.lab40.zipkindemo.producer.DemoProducer;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private DemoProducer producer;\n\n    @GetMapping(\"/rabbitmq\")\n    public String echo() {\n        this.sendMessage(1);\n        return \"rabbitmq\";\n    }\n\n    public void sendMessage(Integer id) {\n        producer.syncSend(id);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-rabbitmq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/message/DemoMessage.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.message;\n\nimport java.io.Serializable;\n\npublic class DemoMessage implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_\";\n\n    public static final String EXCHANGE = \"EXCHANGE_DEMO_\";\n\n    public static final String ROUTING_KEY = \"ROUTING_KEY_\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public DemoMessage setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"DemoMessage{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-rabbitmq/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/producer/DemoProducer.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.producer;\n\nimport cn.iocoder.springboot.lab40.zipkindemo.message.DemoMessage;\nimport org.springframework.amqp.rabbit.core.RabbitTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class DemoProducer {\n\n    @Autowired\n    private RabbitTemplate rabbitTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 DemoMessage 消息\n        DemoMessage message = new DemoMessage();\n        message.setId(id);\n        // 同步发送消息\n        rabbitTemplate.convertAndSend(DemoMessage.EXCHANGE, DemoMessage.ROUTING_KEY, message);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-rabbitmq/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application-rabbitmq\n\n  # RabbitMQ 配置项，对应 RabbitProperties 配置类\n  rabbitmq:\n    host: 127.0.0.1 # RabbitMQ 服务的地址\n    port: 5672 # RabbitMQ 服务的端口\n    username: guest # RabbitMQ 服务的账号\n    password: guest # RabbitMQ 服务的密码\n"
  },
  {
    "path": "lab-40/lab-40-redis/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-40-redis</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Data Redis 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis</artifactId>\n            <exclusions>\n                <!-- 去掉对 Lettuce 的依赖，因为 Spring Boot 优先使用 Lettuce 作为 Redis 客户端 -->\n                <exclusion>\n                    <groupId>io.lettuce</groupId>\n                    <artifactId>lettuce-core</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <!-- 引入 Jedis 的依赖，这样 Spring Boot 实现对 Jedis 的自动化配置 -->\n        <dependency>\n            <groupId>redis.clients</groupId>\n            <artifactId>jedis</artifactId>\n        </dependency>\n\n        <!-- Brave 核心库 -->\n        <!-- The below are needed to report traces to http://localhost:9411/api/v2/spans -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.zipkin.reporter2</groupId>\n            <artifactId>zipkin-sender-okhttp3</artifactId>\n        </dependency>\n\n        <!-- Adds the MVC class and method names to server spans -->\n        <!-- Brave 对 Spring MVC 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-spring-webmvc</artifactId>\n        </dependency>\n\n        <!--  Brave 对 Opentracing 的实现 -->\n        <dependency>\n            <groupId>io.opentracing.brave</groupId>\n            <artifactId>brave-opentracing</artifactId>\n            <version>0.35.0</version>\n        </dependency>\n\n        <!-- Opentracing 对 Redis 的支持 -->\n        <dependency>\n            <groupId>io.opentracing.contrib</groupId>\n            <artifactId>opentracing-redis-jedis3</artifactId>\n            <version>0.1.14</version>\n        </dependency>\n        <dependency>\n            <groupId>io.opentracing.contrib</groupId>\n            <artifactId>opentracing-redis-spring-data</artifactId>\n            <version>0.1.14</version>\n        </dependency>\n\n    </dependencies>\n\n    <dependencyManagement>\n        <!-- Brave Bom 文件 -->\n        <dependencies>\n            <dependency>\n                <groupId>io.zipkin.brave</groupId>\n                <artifactId>brave-bom</artifactId>\n                <version>5.9.1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n</project>\n"
  },
  {
    "path": "lab-40/lab-40-redis/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/RedisApplication.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class RedisApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(RedisApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-redis/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\n@Import(SpanCustomizingAsyncHandlerInterceptor.class)\npublic class SpringMvcConfiguration implements WebMvcConfigurer {\n\n    @Autowired\n    public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;\n\n    /**\n     * Decorates server spans with application-defined web tags\n     */\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        registry.addInterceptor(webMvcTracingCustomizer);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-redis/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.CurrentSpanCustomizer;\nimport brave.SpanCustomizer;\nimport brave.Tracing;\nimport brave.http.HttpTracing;\nimport brave.opentracing.BraveTracer;\nimport brave.servlet.TracingFilter;\nimport io.opentracing.Tracer;\nimport io.opentracing.contrib.redis.common.TracingConfiguration;\nimport io.opentracing.contrib.redis.spring.data.connection.TracingRedisConnectionFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.data.redis.RedisProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.connection.jedis.JedisConnectionFactory;\nimport zipkin2.Span;\nimport zipkin2.reporter.AsyncReporter;\nimport zipkin2.reporter.Sender;\nimport zipkin2.reporter.okhttp3.OkHttpSender;\n\nimport javax.servlet.Filter;\n\n@Configuration\npublic class ZipkinConfiguration {\n\n    // ==================== 通用配置 ====================\n\n    /**\n     * Configuration for how to send spans to Zipkin\n     */\n    @Bean\n    public Sender sender() {\n        return OkHttpSender.create(\"http://127.0.0.1:9411/api/v2/spans\");\n    }\n\n    /**\n     * Configuration for how to buffer spans into messages for Zipkin\n     */\n    @Bean\n    public AsyncReporter<Span> spanReporter() {\n        return AsyncReporter.create(sender());\n    }\n\n    /**\n     * Controls aspects of tracing such as the service name that shows up in the UI\n     */\n    @Bean\n    public Tracing tracing(@Value(\"${spring.application.name}\") String serviceName) {\n        return Tracing.newBuilder()\n                .localServiceName(serviceName)\n                .spanReporter(spanReporter()).build();\n    }\n\n    @Bean\n    public Tracer openTracer(Tracing tracing) {\n        return BraveTracer.create(tracing);\n    }\n\n    /**\n     * Allows someone to add tags to a span if a trace is in progress\n     */\n    @Bean\n    public SpanCustomizer spanCustomizer(Tracing tracing) {\n        return CurrentSpanCustomizer.create(tracing);\n    }\n\n    // ==================== HTTP 相关 ====================\n\n    /**\n     * Decides how to name and tag spans. By default they are named the same as the http method\n     */\n    @Bean\n    public HttpTracing httpTracing(Tracing tracing) {\n        return HttpTracing.create(tracing);\n    }\n\n    /**\n     * Creates server spans for http requests\n     */\n    @Bean\n    public Filter tracingFilter(HttpTracing httpTracing) {\n        return TracingFilter.create(httpTracing);\n    }\n\n    // ==================== SpringMVC 相关 ====================\n    // @see SpringMvcConfiguration 类上的，@Import(SpanCustomizingAsyncHandlerInterceptor.class)\n\n    // ==================== Redis 相关 ====================\n    @Bean\n    public RedisConnectionFactory redisConnectionFactory(Tracer tracer, RedisProperties redisProperties) {\n        // 创建 JedisConnectionFactory 对象\n        RedisConnectionFactory connectionFactory = new JedisConnectionFactory();\n        // 创建 TracingConfiguration 对象\n        TracingConfiguration tracingConfiguration = new TracingConfiguration.Builder(tracer)\n                // 设置拓展 Tag ，设置 Redis 服务器地址。因为默认情况下，不会在操作 Redis 链路的 Span 上记录 Redis 服务器的地址，所以这里需要设置。\n                .extensionTag(\"Server Address\", redisProperties.getHost() + \":\" + redisProperties.getPort())\n                .build();\n        // 创建 TracingRedisConnectionFactory 对象\n        return new TracingRedisConnectionFactory(connectionFactory, tracingConfiguration);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-redis/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private StringRedisTemplate redisTemplate;\n\n    @GetMapping(\"/redis\")\n    public String redis() {\n        this.get(\"demo\");\n        return \"redis\";\n    }\n\n    public void get(String key) {\n        redisTemplate.opsForValue().get(key);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-redis/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application-redis\n\n  # 对应 RedisProperties 类\n  redis:\n    host: 127.0.0.1\n    port: 6379\n    password: # Redis 服务器密码，默认为空。生产中，一定要设置 Redis 密码！\n    database: 0 # Redis 数据库号，默认为 0 。\n    timeout: 0 # Redis 连接超时时间，单位：毫秒。\n    # 对应 RedisProperties.Jedis 内部类\n    jedis:\n      pool:\n        max-active: 8 # 连接池最大连接数，默认为 8 。使用负数表示没有限制。\n        max-idle: 8 # 默认连接数最小空闲的连接数，默认为 8 。使用负数表示没有限制。\n        min-idle: 0 # 默认连接池最小空闲的连接数，默认为 0 。允许设置 0 和 正数。\n        max-wait: -1 # 连接池最大阻塞等待时间，单位：毫秒。默认为 -1 ，表示不限制。\n"
  },
  {
    "path": "lab-40/lab-40-springmvc/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-40-springmvc</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- Brave 核心库 -->\n        <!-- The below are needed to report traces to http://localhost:9411/api/v2/spans -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.zipkin.reporter2</groupId>\n            <artifactId>zipkin-sender-okhttp3</artifactId>\n        </dependency>\n\n        <!-- Adds the MVC class and method names to server spans -->\n        <!-- Brave 对 Spring MVC 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-spring-webmvc</artifactId>\n        </dependency>\n\n    </dependencies>\n\n    <dependencyManagement>\n        <!-- Brave Bom 文件 -->\n        <dependencies>\n            <dependency>\n                <groupId>io.zipkin.brave</groupId>\n                <artifactId>brave-bom</artifactId>\n                <version>5.9.1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n</project>\n"
  },
  {
    "path": "lab-40/lab-40-springmvc/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/SpringMVCApplication.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SpringMVCApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SpringMVCApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-springmvc/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/SpringMvcConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\n@Import(SpanCustomizingAsyncHandlerInterceptor.class) // 创建拦截器 SpanCustomizingAsyncHandlerInterceptor Bean\npublic class SpringMvcConfiguration implements WebMvcConfigurer {\n\n    @Autowired\n    public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;\n\n    /**\n     * Decorates server spans with application-defined web tags\n     */\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) { // 记录 SpringMVC 相关信息到 Span 中\n        registry.addInterceptor(webMvcTracingCustomizer);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-springmvc/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/config/ZipkinConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.config;\n\nimport brave.CurrentSpanCustomizer;\nimport brave.SpanCustomizer;\nimport brave.Tracing;\nimport brave.http.HttpTracing;\nimport brave.servlet.TracingFilter;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport zipkin2.Span;\nimport zipkin2.reporter.AsyncReporter;\nimport zipkin2.reporter.Sender;\nimport zipkin2.reporter.okhttp3.OkHttpSender;\n\nimport javax.servlet.Filter;\n\n@Configuration\npublic class ZipkinConfiguration {\n\n    // ==================== 通用配置 ====================\n\n    /**\n     * Configuration for how to send spans to Zipkin\n     */\n    @Bean\n    public Sender sender() { // Sender 采用 HTTP 通信方式\n        return OkHttpSender.create(\"http://127.0.0.1:9411/api/v2/spans\");\n    }\n\n    /**\n     * Configuration for how to buffer spans into messages for Zipkin\n     */\n    @Bean\n    public AsyncReporter<Span> spanReporter() { // 异步 Reporter\n        return AsyncReporter.create(sender());\n    }\n\n    /**\n     * Controls aspects of tracing such as the service name that shows up in the UI\n     */\n    @Bean\n    public Tracing tracing(@Value(\"${spring.application.name}\") String serviceName) {\n        return Tracing.newBuilder()\n                .localServiceName(serviceName) // 应用名\n                .spanReporter(this.spanReporter()).build();\n    }\n\n    /**\n     * Allows someone to add tags to a span if a trace is in progress\n     */\n    @Bean\n    public SpanCustomizer spanCustomizer(Tracing tracing) {\n        return CurrentSpanCustomizer.create(tracing);\n    }\n\n    // ==================== HTTP 相关 ====================\n\n    /**\n     * Decides how to name and tag spans. By default they are named the same as the http method\n     */\n    @Bean\n    public HttpTracing httpTracing(Tracing tracing) {\n        return HttpTracing.create(tracing);\n    }\n\n    /**\n     * Creates server spans for http requests\n     */\n    @Bean\n    public Filter tracingFilter(HttpTracing httpTracing) { // 拦截请求，记录 HTTP 请求的链路信息\n        return TracingFilter.create(httpTracing);\n    }\n\n    // ==================== SpringMVC 相关 ====================\n    // @see SpringMvcConfiguration 类上的，@Import(SpanCustomizingAsyncHandlerInterceptor.class) 。因为 SpanCustomizingAsyncHandlerInterceptor 未提供 public 构造方法\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-springmvc/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @GetMapping(\"/springmvc\")\n    public String echo() {\n        return \"springmvc\";\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-springmvc/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application-springmvc\n"
  },
  {
    "path": "lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-40-zipkin-dubbo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-40-zipkin-dubbo-api</artifactId>\n\n\n</project>\n"
  },
  {
    "path": "lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-api/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/api/UserService.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.api;\n\n/**\n * 用户服务 RPC Service 接口\n */\npublic interface UserService {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    String get(Integer id);\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-api/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/package-info.java",
    "content": "package cn.iocoder.springcloud.labx13;\n"
  },
  {
    "path": "lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-40-zipkin-dubbo-consumer</artifactId>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-40-zipkin-dubbo-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Dubbo 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n        <!-- 使用 Zookeeper 作为注册中心 -->\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-framework</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-recipes</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n\n        <!-- Brave 核心库 -->\n        <!-- The below are needed to report traces to http://localhost:9411/api/v2/spans -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.zipkin.reporter2</groupId>\n            <artifactId>zipkin-sender-okhttp3</artifactId>\n        </dependency>\n\n        <!-- Adds the MVC class and method names to server spans -->\n        <!-- Brave 对 Spring MVC 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-spring-webmvc</artifactId>\n        </dependency>\n\n        <!-- Brave 针对 Dubbo 的插件，实现链路追踪 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-dubbo</artifactId>\n            <version>5.10.1</version>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <!-- Brave Bom 文件 -->\n        <dependencies>\n            <dependency>\n                <groupId>io.zipkin.brave</groupId>\n                <artifactId>brave-bom</artifactId>\n                <version>5.9.1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n</project>\n"
  },
  {
    "path": "lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-consumer/src/main/java/cn/iocoder/springboot/lab40/zpkindemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springboot.lab40.zpkindemo.consumerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-consumer/src/main/java/cn/iocoder/springboot/lab40/zpkindemo/consumerdemo/config/SpringMvcConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zpkindemo.consumerdemo.config;\n\nimport brave.spring.webmvc.SpanCustomizingAsyncHandlerInterceptor;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Import;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\n@Import(SpanCustomizingAsyncHandlerInterceptor.class)\npublic class SpringMvcConfiguration implements WebMvcConfigurer {\n\n    @Autowired\n    public SpanCustomizingAsyncHandlerInterceptor webMvcTracingCustomizer;\n\n    /**\n     * Decorates server spans with application-defined web tags\n     */\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        registry.addInterceptor(webMvcTracingCustomizer);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-consumer/src/main/java/cn/iocoder/springboot/lab40/zpkindemo/consumerdemo/config/ZipkinConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zpkindemo.consumerdemo.config;\n\nimport brave.CurrentSpanCustomizer;\nimport brave.SpanCustomizer;\nimport brave.Tracing;\nimport brave.http.HttpTracing;\nimport brave.servlet.TracingFilter;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport zipkin2.Span;\nimport zipkin2.reporter.AsyncReporter;\nimport zipkin2.reporter.Sender;\nimport zipkin2.reporter.okhttp3.OkHttpSender;\n\nimport javax.servlet.Filter;\n\n@Configuration\npublic class ZipkinConfiguration {\n\n    // ==================== 通用配置 ====================\n\n    /**\n     * Configuration for how to send spans to Zipkin\n     */\n    @Bean\n    public Sender sender() {\n        return OkHttpSender.create(\"http://127.0.0.1:9411/api/v2/spans\");\n    }\n\n    /**\n     * Configuration for how to buffer spans into messages for Zipkin\n     */\n    @Bean\n    public AsyncReporter<Span> spanReporter() {\n        return AsyncReporter.create(sender());\n    }\n\n    /**\n     * Controls aspects of tracing such as the service name that shows up in the UI\n     */\n    @Bean\n    public Tracing tracing(@Value(\"${spring.application.name}\") String serviceName) {\n        return Tracing.newBuilder()\n                .localServiceName(serviceName)\n//                .currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder()\n//                        .addScopeDecorator(MDCScopeDecorator.create()) // puts trace IDs into logs\n//                        .build()\n//                )\n                .spanReporter(spanReporter()).build();\n    }\n\n    /**\n     * Allows someone to add tags to a span if a trace is in progress\n     */\n    @Bean\n    public SpanCustomizer spanCustomizer(Tracing tracing) {\n        return CurrentSpanCustomizer.create(tracing);\n    }\n\n    // ==================== HTTP 相关 ====================\n\n    /**\n     * Decides how to name and tag spans. By default they are named the same as the http method\n     */\n    @Bean\n    public HttpTracing httpTracing(Tracing tracing) {\n        return HttpTracing.create(tracing);\n    }\n\n    /**\n     * Creates server spans for http requests\n     */\n    @Bean\n    public Filter tracingFilter(HttpTracing httpTracing) {\n        return TracingFilter.create(httpTracing);\n    }\n\n    // ==================== SpringMVC 相关 ====================\n    // @see SpringMvcConfiguration 类上的，@Import(SpanCustomizingAsyncHandlerInterceptor.class)\n\n\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-consumer/src/main/java/cn/iocoder/springboot/lab40/zpkindemo/consumerdemo/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab40.zpkindemo.consumerdemo.controller;\n\nimport cn.iocoder.springboot.lab40.zipkindemo.api.UserService;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Reference(protocol = \"dubbo\", version = \"1.0.0\")\n    private UserService userService;\n\n    @GetMapping(\"/get\")\n    public String  get(@RequestParam(\"id\") Integer id) {\n        return userService.get(id);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: user-service-consumer\n\n# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: ${spring.application.name} # 应用名\n  # Dubbo 注册中心配置\n  registry:\n    address: zookeeper://127.0.0.1:2181 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 服务提供者的配置，对应 ConsumerConfig 类\n  consumer:\n    filter: tracing\n"
  },
  {
    "path": "lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-40-zipkin-dubbo-provider</artifactId>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-40-zipkin-dubbo-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Boot 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对 Dubbo 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n        <!-- 使用 Zookeeper 作为注册中心 -->\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-framework</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-recipes</artifactId>\n            <version>2.13.0</version>\n        </dependency>\n\n        <!-- Brave 核心库 -->\n        <!-- The below are needed to report traces to http://localhost:9411/api/v2/spans -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.zipkin.reporter2</groupId>\n            <artifactId>zipkin-sender-okhttp3</artifactId>\n        </dependency>\n\n        <!-- Brave 针对 Dubbo 的插件，实现链路追踪 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-dubbo</artifactId>\n            <version>5.10.1</version>\n        </dependency>\n    </dependencies>\n\n    <dependencyManagement>\n        <!-- Brave Bom 文件 -->\n        <dependencies>\n            <dependency>\n                <groupId>io.zipkin.brave</groupId>\n                <artifactId>brave-bom</artifactId>\n                <version>5.9.1</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n</project>\n"
  },
  {
    "path": "lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-provider/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/providerdemo/ProviderApplication.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.providerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProviderApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-provider/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/providerdemo/config/ZipkinConfiguration.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.providerdemo.config;\n\nimport brave.CurrentSpanCustomizer;\nimport brave.SpanCustomizer;\nimport brave.Tracing;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport zipkin2.Span;\nimport zipkin2.reporter.AsyncReporter;\nimport zipkin2.reporter.Sender;\nimport zipkin2.reporter.okhttp3.OkHttpSender;\n\n@Configuration\npublic class ZipkinConfiguration {\n\n    // ==================== 通用配置 ====================\n\n    /**\n     * Configuration for how to send spans to Zipkin\n     */\n    @Bean\n    public Sender sender() {\n        return OkHttpSender.create(\"http://127.0.0.1:9411/api/v2/spans\");\n    }\n\n    /**\n     * Configuration for how to buffer spans into messages for Zipkin\n     */\n    @Bean\n    public AsyncReporter<Span> spanReporter() {\n        return AsyncReporter.create(sender());\n    }\n\n    /**\n     * Controls aspects of tracing such as the service name that shows up in the UI\n     */\n    @Bean\n    public Tracing tracing(@Value(\"${spring.application.name}\") String serviceName) {\n        return Tracing.newBuilder()\n                .localServiceName(serviceName)\n//                .currentTraceContext(ThreadLocalCurrentTraceContext.newBuilder()\n//                        .addScopeDecorator(MDCScopeDecorator.create()) // puts trace IDs into logs\n//                        .build()\n//                )\n                .spanReporter(spanReporter()).build();\n    }\n\n    /**\n     * Allows someone to add tags to a span if a trace is in progress\n     */\n    @Bean\n    public SpanCustomizer spanCustomizer(Tracing tracing) {\n        return CurrentSpanCustomizer.create(tracing);\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-provider/src/main/java/cn/iocoder/springboot/lab40/zipkindemo/providerdemo/service/UserServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab40.zipkindemo.providerdemo.service;\n\nimport cn.iocoder.springboot.lab40.zipkindemo.api.UserService;\n\n@org.apache.dubbo.config.annotation.Service(version = \"1.0.0\")\npublic class UserServiceImpl implements UserService {\n\n    @Override\n    public String get(Integer id) {\n        return \"user:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "lab-40/lab-40-zipkin-dubbo/lab-40-zipkin-dubbo-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: user-service-provider\n\n# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: ${spring.application.name} # 应用名\n  # Dubbo 注册中心配\n  registry:\n    address: zookeeper://127.0.0.1:2181 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springboot.lab40.zipkindemo.providerdemo.service\n  # Dubbo 服务提供者的配置，对应 ProviderConfig 类\n  provider:\n    filter: tracing\n"
  },
  {
    "path": "lab-40/lab-40-zipkin-dubbo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-40</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-40-zipkin-dubbo</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-40-zipkin-dubbo-api</module>\n        <module>lab-40-zipkin-dubbo-provider</module>\n        <module>lab-40-zipkin-dubbo-consumer</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-40/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-40</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-40-demo</module>\n        <module>lab-40-springmvc</module>\n        <module>lab-40-zipkin-dubbo</module>\n\n        <module>lab-40-mysql</module>\n        <module>lab-40-redis</module>\n        <module>lab-40-mongodb</module>\n        <module>lab-40-elasticsearch</module>\n\n        <module>lab-40-kafka</module>\n        <module>lab-40-rabbitmq</module>\n        <module>lab-40-activemq</module>\n\n        <module>lab-40-logback</module>\n        <module>lab-40-opentracing</module>\n\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-40/《芋道 Spring Boot 链路追踪 Zipkin 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Zipkin/?github>\n"
  },
  {
    "path": "lab-41/deploy.sh",
    "content": "#!/bin/bash\nset -e\n\n# 基础\n# export JAVA_HOME=/work/programs/jdk/jdk1.8.0_181\n# export PATH=PATH=$PATH:$JAVA_HOME/bin\n# export CLASSPATH=$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar\n\nDATE=$(date +%Y%m%d%H%M)\n# 基础路径\nBASE_PATH=/work/projects/lab-41-demo01\n# 编译后 jar 的地址。部署时，Jenkins 会上传 jar 包到该目录下\nSOURCE_PATH=$BASE_PATH/build\n# 服务名称。同时约定部署服务的 jar 包名字也为它。\nSERVER_NAME=lab-41-demo01\n# 环境\nPROFILES_ACTIVE=prod\n# 健康检查 URL\nHEALTH_CHECK_URL=http://127.0.0.1:8078/actuator/health/\n\n# heapError 存放路径\nHEAP_ERROR_PATH=$BASE_PATH/heapError\n# JVM 参数\nJAVA_OPS=\"-Xms1024m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$HEAP_ERROR_PATH\"\n# JavaAgent 参数。可用于配置 SkyWalking 等链路追踪\nJAVA_AGENT=\n\n# 备份\nfunction backup() {\n    # 如果不存在，则无需备份\n    if [ ! -f \"$BASE_PATH/$SERVER_NAME.jar\" ]; then\n        echo \"[backup] $BASE_PATH/$SERVER_NAME.jar 不存在，跳过备份\"\n    # 如果存在，则备份到 backup 目录下，使用时间作为后缀\n    else\n        echo \"[backup] 开始备份 $SERVER_NAME ...\"\n        cp $BASE_PATH/$SERVER_NAME.jar $BASE_PATH/backup/$SERVER_NAME-$DATE.jar\n        echo \"[backup] 备份 $SERVER_NAME 完成\"\n    fi\n}\n\n# 最新构建代码 移动到项目环境\nfunction transfer() {\n    echo \"[transfer] 开始转移 $SERVER_NAME.jar\"\n\n    # 删除原 jar 包\n    if [ ! -f \"$BASE_PATH/$SERVER_NAME.jar\" ]; then\n        echo \"[transfer] $BASE_PATH/$SERVER_NAME.jar 不存在，跳过删除\"\n    else\n        echo \"[transfer] 移除 $BASE_PATH/$SERVER_NAME.jar 完成\"\n        rm $BASE_PATH/$SERVER_NAME.jar\n    fi\n\n    # 复制新 jar 包\n    echo \"[transfer] 从 $SOURCE_PATH 中获取 $SERVER_NAME.jar 并迁移至 $BASE_PATH ....\"\n    cp $SOURCE_PATH/$SERVER_NAME.jar $BASE_PATH\n\n    echo \"[transfer] 转移 $SERVER_NAME.jar 完成\"\n}\n\n# 停止\nfunction stop() {\n    echo \"[stop] 开始停止 $BASE_PATH/$SERVER_NAME\"\n    PID=$(ps -ef | grep $BASE_PATH/$SERVER_NAME | grep -v \"grep\" | awk '{print $2}')\n    # 如果 Java 服务启动中，则进行关闭\n    if [ -n \"$PID\" ]; then\n        # 正常关闭\n        echo \"[stop] $BASE_PATH/$SERVER_NAME 运行中，开始 kill [$PID]\"\n        kill -15 $PID\n        # 等待最大 60 秒，直到关闭完成。\n        for ((i = 0; i < 60; i++))\n            do\n                sleep 1\n                PID=$(ps -ef | grep $BASE_PATH/$SERVER_NAME | grep -v \"grep\" | awk '{print $2}')\n                if [ -n \"$PID\" ]; then\n                    echo -e \".\\c\"\n                else\n                    echo '[stop] 停止 $BASE_PATH/$SERVER_NAME 成功'\n                    break\n                fi\n\t\t    done\n\n        # 如果正常关闭失败，那么进行强制 kill -9 进行关闭\n        if [ -n \"$PID\" ]; then\n            echo \"[stop] $BASE_PATH/$SERVER_NAME 失败，强制 kill -9 $PID\"\n            kill -9 $PID\n        fi\n    # 如果 Java 服务未启动，则无需关闭\n    else\n        echo \"[stop] $BASE_PATH/$SERVER_NAME 未启动，无需停止\"\n    fi\n}\n\n# 启动\nfunction start() {\n    # 开启启动前，打印启动参数\n    echo \"[start] 开始启动 $BASE_PATH/$SERVER_NAME\"\n    echo \"[start] JAVA_OPS: $JAVA_OPS\"\n    echo \"[start] JAVA_AGENT: $JAVA_AGENT\"\n    echo \"[start] PROFILES: $PROFILES_ACTIVE\"\n\n    # 开始启动\n    BUILD_ID=dontKillMe nohup java -server $JAVA_OPS $JAVA_AGENT -jar $BASE_PATH/$SERVER_NAME.jar --spring.profiles.active=$PROFILES_ACTIVE &\n    echo \"[start] 启动 $BASE_PATH/$SERVER_NAME 完成\"\n}\n\n# 健康检查\nfunction healthCheck() {\n    # 如果配置健康检查，则进行健康检查\n    if [ -n \"$HEALTH_CHECK_URL\" ]; then\n        # 健康检查最大 60 秒，直到健康检查通过\n        echo \"[healthCheck] 开始通过 $HEALTH_CHECK_URL 地址，进行健康检查\";\n        for ((i = 0; i < 60; i++))\n            do\n                # 请求健康检查地址，只获取状态码。\n                result=`curl -I -m 10 -o /dev/null -s -w %{http_code} $HEALTH_CHECK_URL || echo \"000\"`\n                # 如果状态码为 200，则说明健康检查通过\n                if [ \"$result\" == \"200\" ]; then\n                    echo \"[healthCheck] 健康检查通过\";\n                    break\n                # 如果状态码非 200，则说明未通过。sleep 1 秒后，继续重试\n                else\n                    echo -e \".\\c\"\n                    sleep 1\n                fi\n            done\n\n        # 健康检查未通过，则异常退出 shell 脚本，不继续部署。\n        if [ ! \"$result\" == \"200\" ]; then\n            echo \"[healthCheck] 健康检查不通过，可能部署失败。查看日志，自行判断是否启动成功\";\n            tail -n 10 nohup.out\n            exit 1;\n        # 健康检查通过，打印最后 10 行日志，可能部署的人想看下日志。\n        else\n            tail -n 10 nohup.out\n        fi\n    # 如果未配置健康检查，则 slepp 60 秒，人工看日志是否部署成功。\n    else\n        echo \"[healthCheck] HEALTH_CHECK_URL 未配置，开始 sleep 60 秒\";\n        sleep 60\n        echo \"[healthCheck] sleep 60 秒完成，查看日志，自行判断是否启动成功\";\n        tail -n 50 nohup.out\n    fi\n}\n\n# 部署\nfunction deploy() {\n    cd $BASE_PATH\n    # 备份原 jar\n    backup\n    # 停止 Java 服务\n    stop\n    # 部署新 jar\n    transfer\n    # 启动 Java 服务\n    start\n    # 健康检查\n    healthCheck\n}\n\ndeploy\n"
  },
  {
    "path": "lab-41/lab-41-demo01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-41-demo01</artifactId>\n    <packaging>jar</packaging>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <!-- 设置构建的 jar 包名 -->\n        <finalName>${project.artifactId}</finalName>\n        <!-- 使用 spring-boot-maven-plugin 插件打包 -->\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-41/lab-41-demo01/src/main/java/cn/iocoder/springboot/lab40/jenkinsdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab40.jenkinsdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.event.ContextClosedEvent;\nimport org.springframework.stereotype.Component;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n    @Component\n    public class Listener implements ApplicationListener<ApplicationEvent> {\n\n        @Override\n        public void onApplicationEvent(ApplicationEvent event) {\n            if (event instanceof ContextClosedEvent) {\n                this.sleep(10);\n            }\n        }\n\n        private void sleep(int seconds) {\n            try {\n                Thread.sleep(seconds * 1000L);\n            } catch (InterruptedException ignore) {\n            }\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-41/lab-41-demo01/src/main/java/cn/iocoder/springboot/lab40/jenkinsdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab40.jenkinsdemo.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return \"echo\";\n    }\n\n}\n"
  },
  {
    "path": "lab-41/lab-41-demo01/src/main/resources/application-dev.yaml",
    "content": "server:\n  port: 8079\n\nmanagement:\n  server:\n    port: 8078 # 自定义端口，避免 Nginx 暴露出去\n\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n"
  },
  {
    "path": "lab-41/lab-41-demo01/src/main/resources/application-local.yaml",
    "content": "server:\n  port: 8079\n\nmanagement:\n  server:\n    port: 8078 # 自定义端口，避免 Nginx 暴露出去\n\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n"
  },
  {
    "path": "lab-41/lab-41-demo01/src/main/resources/application-pre.yaml",
    "content": "server:\n  port: 8079\n\nmanagement:\n  server:\n    port: 8078 # 自定义端口，避免 Nginx 暴露出去\n\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n"
  },
  {
    "path": "lab-41/lab-41-demo01/src/main/resources/application-prod.yaml",
    "content": "server:\n  port: 8079\n\nmanagement:\n  server:\n    port: 8078 # 自定义端口，避免 Nginx 暴露出去\n\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n"
  },
  {
    "path": "lab-41/lab-41-demo01/src/main/resources/application-uat.yaml",
    "content": "server:\n  port: 8079\n\nmanagement:\n  server:\n    port: 8078 # 自定义端口，避免 Nginx 暴露出去\n\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n"
  },
  {
    "path": "lab-41/lab-41-demo02/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-41-demo02</artifactId>\n    <version>1.0.0</version>\n    <packaging>jar</packaging>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <!-- 设置构建的 jar 包名 -->\n        <finalName>${project.artifactId}</finalName>\n        <!-- 使用 spring-boot-maven-plugin 插件打包 -->\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-41/lab-41-demo02/src/main/java/cn/iocoder/springboot/lab40/jenkinsdemo/Demo02Application.java",
    "content": "package cn.iocoder.springboot.lab40.jenkinsdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Demo02Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Demo02Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-41/lab-41-demo02/src/main/java/cn/iocoder/springboot/lab40/jenkinsdemo/actuate/ServerHealthIndicator.java",
    "content": "package cn.iocoder.springboot.lab40.jenkinsdemo.actuate;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot.actuate.health.AbstractHealthIndicator;\nimport org.springframework.boot.actuate.health.Health;\nimport org.springframework.boot.context.event.ApplicationFailedEvent;\nimport org.springframework.boot.context.event.ApplicationReadyEvent;\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.event.ContextClosedEvent;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ServerHealthIndicator extends AbstractHealthIndicator implements ApplicationListener<ApplicationEvent> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    /**\n     * 是否在服务中\n     */\n    private volatile boolean inService = false;\n\n    @Override\n    protected void doHealthCheck(Health.Builder builder) {\n        if (inService) {\n            builder.up();\n        } else {\n            builder.down();\n        }\n    }\n\n    @Override\n    public void onApplicationEvent(ApplicationEvent event) {\n        if (event instanceof ApplicationReadyEvent) {\n            this.handleApplicationReadyEvent((ApplicationReadyEvent) event);\n        } else if (event instanceof ApplicationFailedEvent) {\n            this.handleApplicationFailedEvent((ApplicationFailedEvent) event);\n        } else if (event instanceof ContextClosedEvent) {\n            this.handleContextClosedEvent((ContextClosedEvent) event);\n        }\n    }\n\n    @SuppressWarnings(\"unused\")\n    private void handleApplicationReadyEvent(ApplicationReadyEvent event) {\n        this.inService = true;\n    }\n\n    @SuppressWarnings(\"unused\")\n    private void handleApplicationFailedEvent(ApplicationFailedEvent event) {\n        this.inService = false;\n    }\n\n    @SuppressWarnings(\"unused\")\n    private void handleContextClosedEvent(ContextClosedEvent event) {\n        // 标记不提供服务\n        this.inService = false;\n\n        // sleep 等待负载均衡完成健康检查\n        for (int i = 0; i < 20; i++) { // TODO 20 需要配置\n            logger.info(\"[handleContextClosedEvent][优雅关闭，第 {} sleep 等待负载均衡完成健康检查]\", i);\n            try {\n                Thread.sleep(1000L);\n            } catch (InterruptedException ignore) {\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-41/lab-41-demo02/src/main/java/cn/iocoder/springboot/lab40/jenkinsdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab40.jenkinsdemo.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return \"echo\";\n    }\n\n}\n"
  },
  {
    "path": "lab-41/lab-41-demo02/src/main/resources/application-dev.yaml",
    "content": "server:\n  port: 8079\n\nmanagement:\n  server:\n    port: 8078 # 自定义端口，避免 Nginx 暴露出去\n\n  endpoint:\n    health:\n      show-details: always # 配置展示明细，这样自定义的 ServerHealthIndicator 才可以被访问\n\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n"
  },
  {
    "path": "lab-41/lab-41-demo02/src/main/resources/application-local.yaml",
    "content": "server:\n  port: 8079\n\nmanagement:\n  server:\n    port: 8078 # 自定义端口，避免 Nginx 暴露出去\n\n  endpoint:\n    health:\n      show-details: always # 配置展示明细，这样自定义的 ServerHealthIndicator 才可以被访问\n\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n"
  },
  {
    "path": "lab-41/lab-41-demo02/src/main/resources/application-pre.yaml",
    "content": "server:\n  port: 8079\n\nmanagement:\n  server:\n    port: 8078 # 自定义端口，避免 Nginx 暴露出去\n\n  endpoint:\n    health:\n      show-details: always # 配置展示明细，这样自定义的 ServerHealthIndicator 才可以被访问\n\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n"
  },
  {
    "path": "lab-41/lab-41-demo02/src/main/resources/application-prod.yaml",
    "content": "server:\n  port: 8079\n\nmanagement:\n  server:\n    port: 8078 # 自定义端口，避免 Nginx 暴露出去\n\n  endpoint:\n    health:\n      show-details: always # 配置展示明细，这样自定义的 ServerHealthIndicator 才可以被访问\n\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n"
  },
  {
    "path": "lab-41/lab-41-demo02/src/main/resources/application-uat.yaml",
    "content": "server:\n  port: 8079\n\nmanagement:\n  server:\n    port: 8078 # 自定义端口，避免 Nginx 暴露出去\n\n  endpoint:\n    health:\n      show-details: always # 配置展示明细，这样自定义的 ServerHealthIndicator 才可以被访问\n\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\n"
  },
  {
    "path": "lab-41/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-41</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-41-demo01</module>\n        <module>lab-41-demo02</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-41/《芋道 Spring Boot 持续交付 Jenkins 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Jenkins/?github>\n"
  },
  {
    "path": "lab-42/lab-42-demo01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-42-demo01</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.46</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.h2database</groupId> <!-- 单元测试，我们采用 H2 作为数据库 -->\n            <artifactId>h2</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-42/lab-42-demo01/src/main/java/cn/iocoder/springboot/lab23/testdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-42/lab-42-demo01/src/main/java/cn/iocoder/springboot/lab23/testdemo/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo.controller;\n\nimport cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO;\nimport cn.iocoder.springboot.lab23.testdemo.service.UserService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 用户 Controller\n */\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Autowired\n    private UserService userService;\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @GetMapping(\"/get\") // URL 修改成 /get\n    public UserDO get(@RequestParam(\"id\") Integer id) {\n        // 查询并返回用户\n        return userService.get(id);\n    }\n\n}\n"
  },
  {
    "path": "lab-42/lab-42-demo01/src/main/java/cn/iocoder/springboot/lab23/testdemo/dao/UserDao.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo.dao;\n\nimport cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.BeanPropertyRowMapper;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic class UserDao {\n\n    @Autowired\n    private JdbcTemplate template;\n\n    public UserDO selectById(Integer id) {\n        return template.queryForObject(\"SELECT id, username, password FROM t_user WHERE id = ?\",\n                new BeanPropertyRowMapper<>(UserDO.class), // 结果转换成对应的对象\n                id);\n    }\n\n}\n"
  },
  {
    "path": "lab-42/lab-42-demo01/src/main/java/cn/iocoder/springboot/lab23/testdemo/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo.dataobject;\n\n/**\n * 用户 DO\n */\npublic class UserDO {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码（明文）\n     *\n     * ps：生产环境下，千万不要明文噢\n     */\n    private String password;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-42/lab-42-demo01/src/main/java/cn/iocoder/springboot/lab23/testdemo/service/UserService.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo.service;\n\nimport cn.iocoder.springboot.lab23.testdemo.dao.UserDao;\nimport cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class UserService {\n\n    @Autowired\n    private UserDao userDao;\n\n    public UserDO get(Integer id) {\n        return userDao.selectById(id);\n    }\n\n}\n"
  },
  {
    "path": "lab-42/lab-42-demo01/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/lab-39-mysql?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n"
  },
  {
    "path": "lab-42/lab-42-demo01/src/test/java/cn/iocoder/springboot/lab23/testdemo/controller/UserControllerTest.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo.controller;\n\nimport cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO;\nimport cn.iocoder.springboot.lab23.testdemo.service.UserService;\nimport org.hamcrest.core.IsEqual;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mockito;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.boot.test.mock.mockito.MockBean;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.ResultActions;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers;\n\n/**\n * UserController 单元测试\n */\n@RunWith(SpringRunner.class)\n@SpringBootTest\n@AutoConfigureMockMvc\npublic class UserControllerTest {\n\n    @Autowired\n    private MockMvc mvc;\n\n    @MockBean\n    private UserService userService;\n\n    @Test\n    public void testGet() throws Exception {\n        // Mock UserService 的 get 方法\n        Mockito.when(userService.get(1)).thenReturn(\n                new UserDO().setId(1).setUsername(\"username:1\").setPassword(\"password:1\"));\n\n        // 查询用户\n        ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.get(\"/user/get?id=1\"));\n\n        // 校验响应状态码\n        resultActions.andExpect(MockMvcResultMatchers.status().isOk()); // 响应状态码 200\n\n        // 校验响应内容方式一：直接全部匹配\n        resultActions.andExpect(MockMvcResultMatchers.content().json(\"{\\n\" +\n                \"    \\\"id\\\": 1,\\n\" +\n                \"    \\\"username\\\": \\\"username:1\\\",\\n\" +\n                \"    \\\"password\\\": \\\"password:1\\\"\\n\" +\n                \"}\", true)); // 响应结果\n\n        // 校验响应内容方式二：逐个字段匹配\n        resultActions.andExpect(MockMvcResultMatchers.jsonPath(\"id\", IsEqual.equalTo(1)));\n        resultActions.andExpect(MockMvcResultMatchers.jsonPath(\"username\", IsEqual.equalTo(\"username:1\")));\n        resultActions.andExpect(MockMvcResultMatchers.jsonPath(\"password\", IsEqual.equalTo(\"password:1\")));\n    }\n\n}\n"
  },
  {
    "path": "lab-42/lab-42-demo01/src/test/java/cn/iocoder/springboot/lab23/testdemo/dao/UserDaoTest.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo.dao;\n\nimport cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.jdbc.Sql;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class UserDaoTest {\n\n    @Autowired\n    private UserDao userDao;\n\n    @Test\n    @Sql(scripts = \"/sql/create_tables.sql\", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)\n    @Sql(statements = \"INSERT INTO `t_user`(`id`, `username`, `password`) VALUES (1, 'username:1', 'password:1');\", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)\n    @Sql(scripts = \"/sql/clean.sql\", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)\n    public void testSelectById() {\n        // 查询用户\n        UserDO user = userDao.selectById(1);\n\n        // 校验结果\n        Assert.assertEquals(\"编号不匹配\", 1, (int) user.getId());\n        Assert.assertEquals(\"用户名不匹配\", \"username:1\", user.getUsername());\n        Assert.assertEquals(\"密码不匹配\", \"password:1\", user.getPassword());\n    }\n\n}\n"
  },
  {
    "path": "lab-42/lab-42-demo01/src/test/java/cn/iocoder/springboot/lab23/testdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo;\n"
  },
  {
    "path": "lab-42/lab-42-demo01/src/test/java/cn/iocoder/springboot/lab23/testdemo/service/UserServiceTest.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo.service;\n\nimport cn.iocoder.springboot.lab23.testdemo.dao.UserDao;\nimport cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mockito;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.boot.test.mock.mockito.MockBean;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class UserServiceTest {\n\n    @MockBean\n    private UserDao userDao;\n\n    @Autowired\n    private UserService userService;\n\n    @Test\n    public void testGet() {\n        // Mock UserDao 的 selectById 方法\n        Mockito.when(userDao.selectById(1)).thenReturn(\n                new UserDO().setId(1).setUsername(\"username:1\").setPassword(\"password:1\"));\n\n        // 查询用户\n        UserDO user = userService.get(1);\n\n        // 校验结果\n        Assert.assertEquals(\"编号不匹配\", 1, (int) user.getId());\n        Assert.assertEquals(\"用户名不匹配\", \"username:1\", user.getUsername());\n        Assert.assertEquals(\"密码不匹配\", \"password:1\", user.getPassword());\n    }\n\n}\n"
  },
  {
    "path": "lab-42/lab-42-demo01/src/test/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    url: jdbc:h2:mem:testdb\n    driver-class-name: org.h2.Driver\n    username: sa\n    password:\n"
  },
  {
    "path": "lab-42/lab-42-demo01/src/test/resources/sql/clean.sql",
    "content": "DROP TABLE `t_user`\n"
  },
  {
    "path": "lab-42/lab-42-demo01/src/test/resources/sql/create_tables.sql",
    "content": "CREATE TABLE `t_user` (\n   `id` INT AUTO_INCREMENT  PRIMARY KEY COMMENT '用户编号',\n   `username` VARCHAR(64) NOT NULL COMMENT '账号',\n   `password` VARCHAR(64) NOT NULL COMMENT '密码'\n);\n"
  },
  {
    "path": "lab-42/lab-42-demo02/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-42-demo02</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.46</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.h2database</groupId> <!-- 单元测试，我们采用 H2 作为数据库 -->\n            <artifactId>h2</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-42/lab-42-demo02/src/main/java/cn/iocoder/springboot/lab23/testdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-42/lab-42-demo02/src/main/java/cn/iocoder/springboot/lab23/testdemo/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo.controller;\n\nimport cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO;\nimport cn.iocoder.springboot.lab23.testdemo.service.UserService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 用户 Controller\n */\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Autowired\n    private UserService userService;\n\n    /**\n     * 获得指定用户编号的用户\n     *\n     * @param id 用户编号\n     * @return 用户\n     */\n    @GetMapping(\"/get\") // URL 修改成 /get\n    public UserDO get(@RequestParam(\"id\") Integer id) {\n        // 查询并返回用户\n        return userService.get(id);\n    }\n\n}\n"
  },
  {
    "path": "lab-42/lab-42-demo02/src/main/java/cn/iocoder/springboot/lab23/testdemo/dao/UserDao.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo.dao;\n\nimport cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.BeanPropertyRowMapper;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic class UserDao {\n\n    @Autowired\n    private JdbcTemplate template;\n\n    public UserDO selectById(Integer id) {\n        return template.queryForObject(\"SELECT id, username, password FROM t_user WHERE id = ?\",\n                new BeanPropertyRowMapper<>(UserDO.class), // 结果转换成对应的对象\n                id);\n    }\n\n    public UserDO selectByUsername(String username) {\n        return new UserDO().setId(1)\n                .setUsername(username)\n                .setPassword(null);\n    }\n\n}\n"
  },
  {
    "path": "lab-42/lab-42-demo02/src/main/java/cn/iocoder/springboot/lab23/testdemo/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo.dataobject;\n\n/**\n * 用户 DO\n */\npublic class UserDO {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码（明文）\n     *\n     * ps：生产环境下，千万不要明文噢\n     */\n    private String password;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-42/lab-42-demo02/src/main/java/cn/iocoder/springboot/lab23/testdemo/service/UserService.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo.service;\n\nimport cn.iocoder.springboot.lab23.testdemo.dao.UserDao;\nimport cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class UserService {\n\n    @Autowired\n    private UserDao userDao;\n\n    public UserDO get(Integer id) {\n        return userDao.selectById(id);\n    }\n\n    public boolean exists(String username) {\n        return true;\n    }\n\n    public Integer add(String username, String password) {\n        if (userDao.selectByUsername(username) != null) {\n            return null;\n        }\n        return 1;\n    }\n\n}\n"
  },
  {
    "path": "lab-42/lab-42-demo02/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/lab-39-mysql?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n"
  },
  {
    "path": "lab-42/lab-42-demo02/src/test/java/cn/iocoder/springboot/lab23/testdemo/controller/UserControllerTest.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo.controller;\n\nimport cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO;\nimport cn.iocoder.springboot.lab23.testdemo.service.UserService;\nimport org.hamcrest.core.IsEqual;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mockito;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.boot.test.mock.mockito.MockBean;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport org.springframework.test.web.servlet.MockMvc;\nimport org.springframework.test.web.servlet.ResultActions;\nimport org.springframework.test.web.servlet.request.MockMvcRequestBuilders;\nimport org.springframework.test.web.servlet.result.MockMvcResultMatchers;\n\n/**\n * UserController 单元测试\n */\n@RunWith(SpringRunner.class)\n@SpringBootTest\n@AutoConfigureMockMvc\npublic class UserControllerTest {\n\n    @Autowired\n    private MockMvc mvc;\n\n    @MockBean\n    private UserService userService;\n\n    @Test\n    public void testGet() throws Exception {\n        // Mock UserService 的 get 方法\n        Mockito.when(userService.get(1)).thenReturn(\n                new UserDO().setId(1).setUsername(\"username:1\").setPassword(\"password:1\"));\n\n        // 查询用户\n        ResultActions resultActions = mvc.perform(MockMvcRequestBuilders.get(\"/user/get?id=1\"));\n\n        // 校验响应状态码\n        resultActions.andExpect(MockMvcResultMatchers.status().isOk()); // 响应状态码 200\n\n        // 校验响应内容方式一：直接全部匹配\n        resultActions.andExpect(MockMvcResultMatchers.content().json(\"{\\n\" +\n                \"    \\\"id\\\": 1,\\n\" +\n                \"    \\\"username\\\": \\\"username:1\\\",\\n\" +\n                \"    \\\"password\\\": \\\"password:1\\\"\\n\" +\n                \"}\", true)); // 响应结果\n\n        // 校验响应内容方式二：逐个字段匹配\n        resultActions.andExpect(MockMvcResultMatchers.jsonPath(\"id\", IsEqual.equalTo(1)));\n        resultActions.andExpect(MockMvcResultMatchers.jsonPath(\"username\", IsEqual.equalTo(\"username:1\")));\n        resultActions.andExpect(MockMvcResultMatchers.jsonPath(\"password\", IsEqual.equalTo(\"password:1\")));\n    }\n\n}\n"
  },
  {
    "path": "lab-42/lab-42-demo02/src/test/java/cn/iocoder/springboot/lab23/testdemo/dao/UserDaoTest.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo.dao;\n\nimport cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.jdbc.Sql;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class UserDaoTest {\n\n    @Autowired\n    private UserDao userDao;\n\n    @Test\n    @Sql(scripts = \"/sql/create_tables.sql\", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)\n    @Sql(statements = \"INSERT INTO `t_user`(`id`, `username`, `password`) VALUES (1, 'username:1', 'password:1');\", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)\n    @Sql(scripts = \"/sql/clean.sql\", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)\n    public void testSelectById() {\n        // 查询用户\n        UserDO user = userDao.selectById(1);\n\n        // 校验结果\n        Assert.assertEquals(\"编号不匹配\", 1, (int) user.getId());\n        Assert.assertEquals(\"用户名不匹配\", \"username:1\", user.getUsername());\n        Assert.assertEquals(\"密码不匹配\", \"password:1\", user.getPassword());\n    }\n\n}\n"
  },
  {
    "path": "lab-42/lab-42-demo02/src/test/java/cn/iocoder/springboot/lab23/testdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo;\n"
  },
  {
    "path": "lab-42/lab-42-demo02/src/test/java/cn/iocoder/springboot/lab23/testdemo/service/UserServiceTest.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo.service;\n\nimport cn.iocoder.springboot.lab23.testdemo.dao.UserDao;\nimport cn.iocoder.springboot.lab23.testdemo.dataobject.UserDO;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mockito;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.boot.test.mock.mockito.MockBean;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class UserServiceTest {\n\n    @MockBean\n    private UserDao userDao;\n\n    @Autowired\n    private UserService userService;\n\n    @Test\n    public void testGet() {\n        // Mock UserDao 的 selectById 方法\n        Mockito.when(userDao.selectById(1)).thenReturn(\n                new UserDO().setId(1).setUsername(\"username:1\").setPassword(\"password:1\"));\n\n        // 查询用户\n        UserDO user = userService.get(1);\n\n        // 校验结果\n        Assert.assertEquals(\"编号不匹配\", 1, (int) user.getId());\n        Assert.assertEquals(\"用户名不匹配\", \"username:1\", user.getUsername());\n        Assert.assertEquals(\"密码不匹配\", \"password:1\", user.getPassword());\n    }\n\n}\n"
  },
  {
    "path": "lab-42/lab-42-demo02/src/test/java/cn/iocoder/springboot/lab23/testdemo/service/UserServiceTest2.java",
    "content": "package cn.iocoder.springboot.lab23.testdemo.service;\n\nimport cn.iocoder.springboot.lab23.testdemo.dao.UserDao;\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.mockito.Mockito;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.boot.test.mock.mockito.SpyBean;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class UserServiceTest2 {\n\n    @Autowired\n    private UserService userService;\n\n    @SpyBean\n    private UserDao userDao;\n\n    @Test\n    public void testAddSuccess() {\n        System.out.println(\"testAddSuccess\");\n        // Mock UserService 的 exists 方法\n        Mockito.when(userDao.selectByUsername(\"username\")).thenReturn(null);\n\n        Assert.assertNotNull(\"注册返回为 null，注册失败\",\n                userService.add(\"username\", \"password\"));\n    }\n\n    @Test\n    public void testAddFailure() {\n        System.out.println(\"testAddFailure\");\n\n        Assert.assertNull(\"注册返回为 null，注册失败\",\n                userService.add(\"username\", \"password\"));\n    }\n\n}\n"
  },
  {
    "path": "lab-42/lab-42-demo02/src/test/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容\n  datasource:\n    url: jdbc:h2:mem:testdb\n    driver-class-name: org.h2.Driver\n    username: sa\n    password:\n"
  },
  {
    "path": "lab-42/lab-42-demo02/src/test/resources/sql/clean.sql",
    "content": "DROP TABLE `t_user`\n"
  },
  {
    "path": "lab-42/lab-42-demo02/src/test/resources/sql/create_tables.sql",
    "content": "CREATE TABLE `t_user` (\n   `id` INT AUTO_INCREMENT  PRIMARY KEY COMMENT '用户编号',\n   `username` VARCHAR(64) NOT NULL COMMENT '账号',\n   `password` VARCHAR(64) NOT NULL COMMENT '密码'\n);\n"
  },
  {
    "path": "lab-42/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-42</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-42-demo01</module>\n        <module>lab-42-demo02</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-42/《芋道 Spring Boot 单元测试 Test 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Unit-Test/?github>\n"
  },
  {
    "path": "lab-43/lab-43-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-43-demo</artifactId>\n\n    <dependencies>\n        <!-- Spring Boot Starter 基础依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- Spring Boot 配置处理器 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-configuration-processor</artifactId>\n            <optional>true</optional>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-43/lab-43-demo/src/main/java/cn/iocoder/springboot/lab43/propertydemo/Application.java",
    "content": "package cn.iocoder.springboot.lab43.propertydemo;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.stereotype.Component;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n    @Component\n    public class OrderPropertiesCommandLineRunner implements CommandLineRunner {\n\n        private final Logger logger = LoggerFactory.getLogger(getClass());\n\n        @Autowired\n        private OrderProperties orderProperties;\n\n        @Override\n        public void run(String... args) {\n            logger.info(\"payTimeoutSeconds:\" + orderProperties.getPayTimeoutSeconds());\n            logger.info(\"createFrequencySeconds:\" + orderProperties.getCreateFrequencySeconds());\n        }\n\n    }\n\n    @Component\n    public class ValueCommandLineRunner implements CommandLineRunner {\n\n        private final Logger logger = LoggerFactory.getLogger(getClass());\n\n        @Value(\"${order.pay-timeout-seconds}\")\n        private Integer payTimeoutSeconds;\n\n        @Value(\"${order.create-frequency-seconds}\")\n        private Integer createFrequencySeconds;\n\n//        @Value(\"${order.desc}\")\n//        private String desc;\n\n        @Override\n        public void run(String... args) {\n            logger.info(\"payTimeoutSeconds:\" + payTimeoutSeconds);\n            logger.info(\"createFrequencySeconds:\" + createFrequencySeconds);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-43/lab-43-demo/src/main/java/cn/iocoder/springboot/lab43/propertydemo/OrderProperties.java",
    "content": "package cn.iocoder.springboot.lab43.propertydemo;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n//    /**\n//     * 配置描述\n//     */\n//    private String desc;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n//    public String getDesc() {\n//        return desc;\n//    }\n//\n//    public OrderProperties setDesc(String desc) {\n//        this.desc = desc;\n//        return this;\n//    }\n\n}\n"
  },
  {
    "path": "lab-43/lab-43-demo/src/main/resources/application.yaml",
    "content": "order:\n  pay-timeout-seconds: 120 # 订单支付超时时长，单位：秒。\n  create-frequency-seconds: 10 # 订单创建频率，单位：秒\n#  desc: \"订单支付超时时长为 ${order.pay-timeout-seconds} 秒，订单创建频率为 ${order.create-frequency-seconds} 秒\"\n"
  },
  {
    "path": "lab-43/lab-43-demo-configname/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-43-demo-configname</artifactId>\n\n    <dependencies>\n        <!-- Spring Boot Starter 基础依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-43/lab-43-demo-configname/src/main/java/cn/iocoder/springboot/lab43/propertydemo/Application.java",
    "content": "package cn.iocoder.springboot.lab43.propertydemo;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.context.config.ConfigFileApplicationListener;\nimport org.springframework.stereotype.Component;\n\n@SpringBootApplication\npublic class Application {\n\n    /**\n     * 设置需要读取的配置文件的名字。\n     * 基于 {@link org.springframework.boot.context.config.ConfigFileApplicationListener#CONFIG_NAME_PROPERTY} 实现。\n     */\n    private static final String CONFIG_NAME_VALUE = \"application,rpc\";\n\n    public static void main(String[] args) {\n        // 设置环境变量\n        System.setProperty(ConfigFileApplicationListener.CONFIG_NAME_PROPERTY, CONFIG_NAME_VALUE);\n\n        // 启动 Spring Boot 应用\n        SpringApplication.run(Application.class, args);\n    }\n\n    @Component\n    public class ValueCommandLineRunner implements CommandLineRunner {\n\n        private final Logger logger = LoggerFactory.getLogger(getClass());\n\n        @Value(\"${application-test}\")\n        private String applicationTest;\n\n        @Value(\"${rpc-test}\")\n        private String rpcTest;\n\n        @Override\n        public void run(String... args) {\n            logger.info(\"applicationTest:\" + applicationTest);\n            logger.info(\"rpcTest:\" + rpcTest);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-43/lab-43-demo-configname/src/main/resources/application.yaml",
    "content": "application-test: hahaha\n"
  },
  {
    "path": "lab-43/lab-43-demo-configname/src/main/resources/rpc.yaml",
    "content": "rpc-test: yeah\n"
  },
  {
    "path": "lab-43/lab-43-demo-jasypt/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-43-demo-jasypt</artifactId>\n\n    <dependencies>\n        <!-- Spring Boot Starter 基础依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对 Jasypt 实现自动化配置 -->\n        <dependency>\n            <groupId>com.github.ulisesbocchio</groupId>\n            <artifactId>jasypt-spring-boot-starter</artifactId>\n            <version>3.0.2</version>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-43/lab-43-demo-jasypt/src/main/java/cn/iocoder/springboot/lab43/propertydemo/Application.java",
    "content": "package cn.iocoder.springboot.lab43.propertydemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-43/lab-43-demo-jasypt/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n#    name: ENC(xQZuD8KnkqzIGep0FFH0DYJ3Re9TrKTdvu2fxIlWNpwFcdNGhkpCag==)\n#    name: ENC(KoaHnIhRGiCdWh0T2lby899Cov6MyiAXrW5PadJ3XFY=)\n    name: demo-application\n\njasypt:\n  # jasypt 配置项，对应 JasyptEncryptorConfigurationProperties 配置类\n  encryptor:\n    algorithm: PBEWithMD5AndDES # 加密算法\n    password: ${JASYPT_PASSWORD} # 加密秘钥\n"
  },
  {
    "path": "lab-43/lab-43-demo-jasypt/src/test/java/cn/iocoder/springboot/lab43/propertydemo/JasyptTest.java",
    "content": "package cn.iocoder.springboot.lab43.propertydemo;\n\nimport org.jasypt.encryption.StringEncryptor;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class JasyptTest {\n\n    @Autowired\n    private StringEncryptor encryptor;\n\n    @Test\n    public void encode() {\n        String applicationName = \"demo-application\";\n        System.out.println(encryptor.encrypt(applicationName));\n    }\n\n    @Value(\"${spring.application.name}\")\n    private String applicationName;\n\n    @Test\n    public void print() {\n        System.out.println(applicationName);\n    }\n\n    @Value(\"${jasypt.encryptor.password}\")\n    private String password;\n\n}\n"
  },
  {
    "path": "lab-43/lab-43-demo-profiles/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-43-demo-profiles</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-43/lab-43-demo-profiles/src/main/java/cn/iocoder/springboot/lab43/propertydemo/ProfilesApplication.java",
    "content": "package cn.iocoder.springboot.lab43.propertydemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProfilesApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProfilesApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-43/lab-43-demo-profiles/src/main/resources/application-dev.yaml",
    "content": "server:\n  port: 8081\n"
  },
  {
    "path": "lab-43/lab-43-demo-profiles/src/main/resources/application-local.yaml",
    "content": "server:\n  port: 8080\n"
  },
  {
    "path": "lab-43/lab-43-demo-profiles/src/main/resources/application-pre.yaml",
    "content": "server:\n  port: 8083\n"
  },
  {
    "path": "lab-43/lab-43-demo-profiles/src/main/resources/application-prod.yaml",
    "content": "server:\n  port: 8084\n"
  },
  {
    "path": "lab-43/lab-43-demo-profiles/src/main/resources/application-uat.yaml",
    "content": "server:\n  port: 8082\n"
  },
  {
    "path": "lab-43/lab-43-demo-profiles/src/main/resources/application.yaml",
    "content": "server:\n  port: 7070\n\nspring:\n  application:\n    name: demo-application\n"
  },
  {
    "path": "lab-43/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-43</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-43-demo</module>\n        <module>lab-43-demo-profiles</module>\n        <module>lab-43-demo-jasypt</module>\n        <module>lab-43-demo-configname</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-43/《芋道 Spring Boot 配置文件入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/config-file/?github>\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-44-nacos-config-demo</artifactId>\n\n    <dependencies>\n        <!-- Spring Boot Starter 基础依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对 Nacos 作为配置中心的自动化配置 -->\n        <dependency>\n            <groupId>com.alibaba.boot</groupId>\n            <artifactId>nacos-config-spring-boot-starter</artifactId>\n            <version>0.2.4</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.stereotype.Component;\n\n@SpringBootApplication\n// @NacosPropertySource(dataId = \"example\", type = ConfigType.YAML)\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n    @Component\n    public class OrderPropertiesCommandLineRunner implements CommandLineRunner {\n\n        private final Logger logger = LoggerFactory.getLogger(getClass());\n\n        @Autowired\n        private OrderProperties orderProperties;\n\n        @Override\n        public void run(String... args) {\n            logger.info(\"payTimeoutSeconds:\" + orderProperties.getPayTimeoutSeconds());\n            logger.info(\"createFrequencySeconds:\" + orderProperties.getCreateFrequencySeconds());\n        }\n\n    }\n\n    @Component\n    public class ValueCommandLineRunner implements CommandLineRunner {\n\n        private final Logger logger = LoggerFactory.getLogger(getClass());\n\n//        @NacosValue(value = \"${order.pay-timeout-seconds}\")\n        @Value(value = \"${order.pay-timeout-seconds}\")\n        private Integer payTimeoutSeconds;\n\n//        @NacosValue(value = \"${order.create-frequency-seconds}\")\n        @Value(value = \"${order.create-frequency-seconds}\")\n        private Integer createFrequencySeconds;\n\n        @Override\n        public void run(String... args) {\n            logger.info(\"payTimeoutSeconds:\" + payTimeoutSeconds);\n            logger.info(\"createFrequencySeconds:\" + createFrequencySeconds);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/OrderProperties.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n//@NacosConfigurationProperties(prefix = \"order\", dataId = \"${nacos.config.data-id}\", type = ConfigType.YAML)\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n//    /**\n//     * 配置描述\n//     */\n//    private String desc;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n//    public String getDesc() {\n//        return desc;\n//    }\n//\n//    public OrderProperties setDesc(String desc) {\n//        this.desc = desc;\n//        return this;\n//    }\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo/src/main/resources/application.yaml",
    "content": "nacos:\n  # Nacos 配置中心的配置项，对应 NacosConfigProperties 配置类\n  config:\n    server-addr: 127.0.0.1:18848 # Nacos 服务器地址\n    bootstrap:\n      enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。\n      log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。\n    data-id: example # 使用的 Nacos 配置集的 dataId。\n    type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。\n    group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP。\n    namespace: # 使用的 Nacos 的命名空间，默认为 null。\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-44-nacos-config-demo-actuator</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Nacos 作为配置中心的自动化配置 -->\n        <dependency>\n            <groupId>com.alibaba.boot</groupId>\n            <artifactId>nacos-config-spring-boot-starter</artifactId>\n            <version>0.2.4</version>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n        <!-- 实现对 Nacos 作为配置中心的 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>com.alibaba.boot</groupId>\n            <artifactId>nacos-config-spring-boot-actuator</artifactId>\n            <version>0.2.4</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-actuator/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n// @NacosPropertySource(dataId = \"example\", type = ConfigType.YAML)\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-actuator/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo.controller;\n\nimport cn.iocoder.springboot.lab44.nacosdemo.properties.TestProperties;\nimport com.alibaba.nacos.api.config.annotation.NacosValue;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n//    @Value(\"${test}\")\n    @NacosValue(value = \"${test}\", autoRefreshed = true)\n    private String test;\n\n    @GetMapping(\"/test\")\n    public String test() {\n        return test;\n    }\n\n    @Autowired\n    private TestProperties testProperties;\n\n    @GetMapping(\"/test_properties\")\n    public TestProperties testProperties() {\n        return testProperties;\n    }\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/logger\")\n    public void logger() {\n        logger.debug(\"[logger][测试一下]\");\n    }\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-actuator/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/listener/LoggingSystemConfigListener.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo.listener;\n\nimport com.alibaba.nacos.api.config.ConfigType;\nimport com.alibaba.nacos.api.config.annotation.NacosConfigListener;\nimport com.alibaba.nacos.spring.util.parse.DefaultYamlConfigParse;\nimport org.springframework.boot.logging.LogLevel;\nimport org.springframework.boot.logging.LoggingSystem;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.Properties;\n\n@Component\npublic class LoggingSystemConfigListener {\n\n    /**\n     * 日志配置项的前缀\n     */\n    private static final String LOGGER_TAG = \"logging.level.\";\n\n    @Resource\n    private LoggingSystem loggingSystem;\n\n    @NacosConfigListener(dataId = \"${nacos.config.data-id}\", type = ConfigType.YAML, timeout = 5000)\n    public void onChange(String newLog) throws Exception {\n        // 使用 DefaultYamlConfigParse 工具类，解析配置\n        Properties properties = new DefaultYamlConfigParse().parse(newLog);\n        // 遍历配置集的每个配置项，判断是否是 logging.level 配置项\n        for (Object t : properties.keySet()) {\n            String key = String.valueOf(t);\n            // 如果是 logging.level 配置项，则设置其对应的日志级别\n            if (key.startsWith(LOGGER_TAG)) {\n                // 获得日志级别\n                String strLevel = properties.getProperty(key, \"info\");\n                LogLevel level = LogLevel.valueOf(strLevel.toUpperCase());\n                // 设置日志级别到 LoggingSystem 中\n                loggingSystem.setLogLevel(key.replace(LOGGER_TAG, \"\"), level);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-actuator/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/properties/TestProperties.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo.properties;\n\nimport com.alibaba.nacos.api.config.ConfigType;\nimport com.alibaba.nacos.api.config.annotation.NacosConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n@NacosConfigurationProperties(prefix = \"\", dataId = \"${nacos.config.data-id}\", type = ConfigType.YAML, autoRefreshed = true)\npublic class TestProperties {\n\n    /**\n     * 测试属性\n     */\n    private String test;\n\n    public String getTest() {\n        return test;\n    }\n\n    public TestProperties setTest(String test) {\n        this.test = test;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-actuator/src/main/resources/application.yaml",
    "content": "nacos:\n  # Nacos 配置中心的配置项，对应 NacosConfigProperties 配置类\n  config:\n    server-addr: 127.0.0.1:18848 # Nacos 服务器地址\n    bootstrap:\n      enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。\n      log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。\n    data-id: example-auto-refresh # 使用的 Nacos 配置集的 dataId。\n    type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。\n    group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP。\n    namespace: # 使用的 Nacos 的命名空间，默认为 null。\n    auto-refresh: true # 是否自动刷新，默认为 false。\n\nmanagement:\n  endpoint:\n    # Health 端点配置项，对应 HealthProperties 配置类\n    health:\n      show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户；可选 ALWAYS 总是展示。\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-auto-refresh/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-44-nacos-config-demo-auto-refresh</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Nacos 作为配置中心的自动化配置 -->\n        <dependency>\n            <groupId>com.alibaba.boot</groupId>\n            <artifactId>nacos-config-spring-boot-starter</artifactId>\n            <version>0.2.4</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-auto-refresh/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n// @NacosPropertySource(dataId = \"example\", type = ConfigType.YAML)\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-auto-refresh/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo.controller;\n\nimport cn.iocoder.springboot.lab44.nacosdemo.properties.TestProperties;\nimport com.alibaba.nacos.api.config.annotation.NacosValue;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n//    @Value(\"${test}\")\n    @NacosValue(value = \"${test}\", autoRefreshed = true)\n    private String test;\n\n    @GetMapping(\"/test\")\n    public String test() {\n        return test;\n    }\n\n    @Autowired\n    private TestProperties testProperties;\n\n    @GetMapping(\"/test_properties\")\n    public TestProperties testProperties() {\n        return testProperties;\n    }\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/logger\")\n    public void logger() {\n        logger.debug(\"[logger][测试一下]\");\n    }\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-auto-refresh/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/listener/LoggingSystemConfigListener.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo.listener;\n\nimport com.alibaba.nacos.api.config.ConfigType;\nimport com.alibaba.nacos.api.config.annotation.NacosConfigListener;\nimport com.alibaba.nacos.spring.util.parse.DefaultYamlConfigParse;\nimport org.springframework.boot.logging.LogLevel;\nimport org.springframework.boot.logging.LoggingSystem;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.Properties;\n\n@Component\npublic class LoggingSystemConfigListener {\n\n    /**\n     * 日志配置项的前缀\n     */\n    private static final String LOGGER_TAG = \"logging.level.\";\n\n    @Resource\n    private LoggingSystem loggingSystem;\n\n    @NacosConfigListener(dataId = \"${nacos.config.data-id}\", type = ConfigType.YAML, timeout = 5000)\n    public void onChange(String newLog) throws Exception {\n        // 使用 DefaultYamlConfigParse 工具类，解析配置\n        Properties properties = new DefaultYamlConfigParse().parse(newLog);\n        // 遍历配置集的每个配置项，判断是否是 logging.level 配置项\n        for (Object t : properties.keySet()) {\n            String key = String.valueOf(t);\n            // 如果是 logging.level 配置项，则设置其对应的日志级别\n            if (key.startsWith(LOGGER_TAG)) {\n                // 获得日志级别\n                String strLevel = properties.getProperty(key, \"info\");\n                LogLevel level = LogLevel.valueOf(strLevel.toUpperCase());\n                // 设置日志级别到 LoggingSystem 中\n                loggingSystem.setLogLevel(key.replace(LOGGER_TAG, \"\"), level);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-auto-refresh/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/properties/TestProperties.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo.properties;\n\nimport com.alibaba.nacos.api.config.ConfigType;\nimport com.alibaba.nacos.api.config.annotation.NacosConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n@NacosConfigurationProperties(prefix = \"\", dataId = \"${nacos.config.data-id}\", type = ConfigType.YAML, autoRefreshed = true)\npublic class TestProperties {\n\n    /**\n     * 测试属性\n     */\n    private String test;\n\n    public String getTest() {\n        return test;\n    }\n\n    public TestProperties setTest(String test) {\n        this.test = test;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-auto-refresh/src/main/resources/application.yaml",
    "content": "nacos:\n  # Nacos 配置中心的配置项，对应 NacosConfigProperties 配置类\n  config:\n    server-addr: 127.0.0.1:18848 # Nacos 服务器地址\n    bootstrap:\n      enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。\n      log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。\n    data-id: example-auto-refresh # 使用的 Nacos 配置集的 dataId。\n    type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。\n    group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP。\n    namespace: # 使用的 Nacos 的命名空间，默认为 null。\n    auto-refresh: true # 是否自动刷新，默认为 false。\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-jasypt/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-44-nacos-config-demo-jasypt</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Nacos 作为配置中心的自动化配置 -->\n        <dependency>\n            <groupId>com.alibaba.boot</groupId>\n            <artifactId>nacos-config-spring-boot-starter</artifactId>\n            <version>0.2.4</version>\n        </dependency>\n\n        <!-- 实现对 Jasypt 实现自动化配置 -->\n        <dependency>\n            <groupId>com.github.ulisesbocchio</groupId>\n            <artifactId>jasypt-spring-boot-starter</artifactId>\n            <version>3.0.2</version>\n<!--            <scope>test</scope>-->\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-jasypt/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-jasypt/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo.controller;\n\nimport com.alibaba.nacos.api.config.annotation.NacosValue;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n//    @Value(\"${spring.application.name}\")\n    @NacosValue(value = \"${spring.application.name}\", autoRefreshed = true)\n    private String applicationName;\n\n    @GetMapping(\"/test\")\n    public String test() {\n        return applicationName;\n    }\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-jasypt/src/main/resources/application.yaml",
    "content": "nacos:\n  # Nacos 配置中心的配置项，对应 NacosConfigProperties 配置类\n  config:\n    server-addr: 127.0.0.1:18848 # Nacos 服务器地址\n    bootstrap:\n      enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。\n      log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。\n    data-id: example-jasypt # 使用的 Nacos 配置集的 dataId。\n    type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。\n    group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP。\n    namespace: # 使用的 Nacos 的命名空间，默认为 null。\n    auto-refresh: true # 是否自动刷新，默认为 false。\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-jasypt/src/test/java/cn/iocoder/springboot/lab44/nacosdemo/JasyptTest.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo;\n\nimport org.jasypt.encryption.StringEncryptor;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class JasyptTest {\n\n    @Autowired\n    private StringEncryptor encryptor;\n\n    @Test\n    public void encode() {\n        // 第一个加密\n        String applicationName = \"demo-application\";\n        System.out.println(encryptor.encrypt(applicationName));\n\n//        // 第二个加密\n//        applicationName = \"demo-app\";\n//        System.out.println(encryptor.encrypt(applicationName));\n    }\n\n    @Value(\"${spring.application.name}\")\n//    @NacosValue(\"${spring.application.name}\")\n    private String applicationName;\n\n    @Test\n    public void print() {\n        System.out.println(applicationName);\n    }\n\n    @Value(\"${jasypt.encryptor.password}\")\n    private String password;\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-multi/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-44-nacos-config-demo-multi</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Nacos 作为配置中心的自动化配置 -->\n        <dependency>\n            <groupId>com.alibaba.boot</groupId>\n            <artifactId>nacos-config-spring-boot-starter</artifactId>\n            <version>0.2.4</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-multi/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.core.env.Environment;\n\n@SpringBootApplication\n// @NacosPropertySource(dataId = \"example\", type = ConfigType.YAML)\npublic class Application {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);\n\n        // 查看 Environment\n        Environment environment = context.getEnvironment();\n        System.out.println(environment);\n    }\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-multi/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.core.env.Environment;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private Environment environment;\n\n    @GetMapping(\"/test\")\n    public void test() {\n        System.out.println(environment);\n    }\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-multi/src/main/resources/application.yaml",
    "content": "nacos:\n  # Nacos 配置中心的配置项，对应 NacosConfigProperties 配置类\n  config:\n    server-addr: 127.0.0.1:18848 # Nacos 服务器地址\n    bootstrap:\n      enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。\n      log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。\n    data-id: example-multi-01 # 使用的 Nacos 配置集的 dataId。\n#    data-ids: example-multi-02\n    type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。\n    group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP。\n    namespace: # 使用的 Nacos 的命名空间，默认为 null。\n    auto-refresh: true # 是否自动刷新，默认为 false。\n    ext-config:\n      - server-addr: 127.0.0.1:18848 # Nacos 服务器地址\n#        data-id: example-multi-11 # 使用的 Nacos 配置集的 dataId。\n        data-ids: example-multi-11, example-multi-12\n        type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP。\n        namespace: # 使用的 Nacos 的命名空间，默认为 null。\n        auto-refresh: true # 是否自动刷新，默认为 false。\n#      - # 这里，可以继续添加。\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-profiles/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-44-nacos-config-demo-profiles</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Nacos 作为配置中心的自动化配置 -->\n        <dependency>\n            <groupId>com.alibaba.boot</groupId>\n            <artifactId>nacos-config-spring-boot-starter</artifactId>\n            <version>0.2.4</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-profiles/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/ProfilesApplication.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProfilesApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProfilesApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-profiles/src/main/resources/application-dev.yaml",
    "content": "nacos:\n  # Nacos 配置中心的配置项，对应 NacosConfigProperties 配置类\n  config:\n    server-addr: 127.0.0.1:18848 # Nacos 服务器地址\n    bootstrap:\n      enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。\n      log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。\n    data-id: example # 使用的 Nacos 配置集的 dataId。\n    type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。\n    group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP。\n    namespace: 14226a0d-799f-424d-8905-162f6a8bf409  # 使用的 Nacos 的命名空间，默认为 null。\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-profiles/src/main/resources/application-prod.yaml",
    "content": "nacos:\n  # Nacos 配置中心的配置项，对应 NacosConfigProperties 配置类\n  config:\n    server-addr: 127.0.0.1:18848 # Nacos 服务器地址\n    bootstrap:\n      enable: true # 是否开启 Nacos 配置预加载功能。默认为 false。\n      log-enable: true # 是否开启 Nacos 支持日志级别的加载时机。默认为 false。\n    data-id: example # 使用的 Nacos 配置集的 dataId。\n    type: YAML # 使用的 Nacos 配置集的配置格式。默认为 PROPERTIES。\n    group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP。\n    namespace: f1686f3b-a984-4cdf-8298-7caee3455d14  # 使用的 Nacos 的命名空间，默认为 null。\n"
  },
  {
    "path": "lab-44/lab-44-nacos-config-demo-profiles/src/main/resources/application.yaml",
    "content": "#server:\n#  port: 7070\n\nspring:\n  application:\n    name: demo-application\n"
  },
  {
    "path": "lab-44/lab-44-nacos-discovery-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-44-nacos-discovery-demo</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Nacos 作为注册中心的自动化配置 -->\n        <dependency>\n            <groupId>com.alibaba.boot</groupId>\n            <artifactId>nacos-discovery-spring-boot-starter</artifactId>\n            <version>0.2.4</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-44/lab-44-nacos-discovery-demo/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-discovery-demo/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/controller/ConsumerController.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo.controller;\n\nimport com.alibaba.nacos.api.annotation.NacosInjected;\nimport com.alibaba.nacos.api.exception.NacosException;\nimport com.alibaba.nacos.api.naming.NamingService;\nimport com.alibaba.nacos.api.naming.pojo.Instance;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.List;\n\n@RestController\n@RequestMapping(\"/consumer\")\npublic class ConsumerController {\n\n    @NacosInjected\n    private NamingService namingService;\n\n    private RestTemplate restTemplate = new RestTemplate();\n\n    @GetMapping(\"/demo\")\n    public String consumer() throws IllegalStateException, NacosException {\n        // 获得实例\n        Instance instance = null;\n        if (false) {\n            List<Instance> instances = namingService.getAllInstances(\"demo-application\");\n            // 获得首个实例，进行调用\n            instance = instances.stream().findFirst()\n                    .orElseThrow(() -> new IllegalStateException(\"未找到对应的 Instance\"));\n        } else {\n            instance = namingService.selectOneHealthyInstance(\"demo-application\");\n        }\n        // 执行请求\n        return restTemplate.getForObject(\"http://\" + instance.toInetAddr() + \"/provider/demo\",\n                String.class);\n    }\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-discovery-demo/src/main/java/cn/iocoder/springboot/lab44/nacosdemo/controller/ProviderController.java",
    "content": "package cn.iocoder.springboot.lab44.nacosdemo.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/provider\")\npublic class ProviderController {\n\n    @GetMapping(\"/demo\")\n    public String provider() {\n        return \"echo\";\n    }\n\n}\n"
  },
  {
    "path": "lab-44/lab-44-nacos-discovery-demo/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application # 应用名\n\nnacos:\n  # Nacos 配置中心的配置项，对应 NacosDiscoveryProperties 配置类\n  discovery:\n    server-addr: 127.0.0.1:18848 # Nacos 服务器地址\n    auto-register: true # 是否自动注册到 Nacos 中。默认为 false。\n    namespace: # 使用的 Nacos 的命名空间，默认为 null。\n    register:\n      service-name: ${spring.application.name} # 注册到 Nacos 的服务名\n      group-name: DEFAULT_GROUP # 使用的 Nacos 服务分组，默认为 DEFAULT_GROUP。\n      cluster-name: # 集群名，默认为空。\n"
  },
  {
    "path": "lab-44/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-44</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-44-nacos-config-demo</module>\n        <module>lab-44-nacos-config-demo-profiles</module>\n        <module>lab-44-nacos-config-demo-auto-refresh</module>\n        <module>lab-44-nacos-config-demo-jasypt</module>\n        <module>lab-44-nacos-config-demo-actuator</module>\n        <module>lab-44-nacos-config-demo-multi</module>\n        <module>lab-44-nacos-discovery-demo</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-44/《芋道 Spring Boot 注册中心 Nacos 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/registry-nacos/?github>\n"
  },
  {
    "path": "lab-44/《芋道 Spring Boot 配置中心 Nacos 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/config-nacos/?github>\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-45-apollo-demo</artifactId>\n\n\n    <dependencies>\n        <!-- Spring Boot Starter 基础依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!--  引入 Apollo 客户端，内置对 Apollo 的自动化配置 -->\n        <dependency>\n            <groupId>com.ctrip.framework.apollo</groupId>\n            <artifactId>apollo-client</artifactId>\n            <version>1.5.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo/src/main/java/cn/iocoder/springboot/lab45/apollodemo/Application.java",
    "content": "package cn.iocoder.springboot.lab45.apollodemo;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.stereotype.Component;\n\n@SpringBootApplication\n// @NacosPropertySource(dataId = \"example\", type = ConfigType.YAML)\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n    @Component\n    public class OrderPropertiesCommandLineRunner implements CommandLineRunner {\n\n        private final Logger logger = LoggerFactory.getLogger(getClass());\n\n        @Autowired\n        private OrderProperties orderProperties;\n\n        @Override\n        public void run(String... args) {\n            logger.info(\"payTimeoutSeconds:\" + orderProperties.getPayTimeoutSeconds());\n            logger.info(\"createFrequencySeconds:\" + orderProperties.getCreateFrequencySeconds());\n        }\n\n    }\n\n    @Component\n    public class ValueCommandLineRunner implements CommandLineRunner {\n\n        private final Logger logger = LoggerFactory.getLogger(getClass());\n\n        @Value(value = \"${order.pay-timeout-seconds}\")\n        private Integer payTimeoutSeconds;\n\n        @Value(value = \"${order.create-frequency-seconds}\")\n        private Integer createFrequencySeconds;\n\n        @Override\n        public void run(String... args) {\n            logger.info(\"payTimeoutSeconds:\" + payTimeoutSeconds);\n            logger.info(\"createFrequencySeconds:\" + createFrequencySeconds);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo/src/main/java/cn/iocoder/springboot/lab45/apollodemo/OrderProperties.java",
    "content": "package cn.iocoder.springboot.lab45.apollodemo;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n//@NacosConfigurationProperties(prefix = \"order\", dataId = \"${nacos.config.data-id}\", type = ConfigType.YAML)\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n//    /**\n//     * 配置描述\n//     */\n//    private String desc;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n//    public String getDesc() {\n//        return desc;\n//    }\n//\n//    public OrderProperties setDesc(String desc) {\n//        this.desc = desc;\n//        return this;\n//    }\n\n}\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo/src/main/resources/application.yaml",
    "content": "server:\n  port: 7070 # 避免和本地的 Apollo Portal 端口冲突\n\napp:\n  id: demo-application # 使用的 Apollo 的项目（应用）编号\napollo:\n  meta: http://127.0.0.1:8080 # Apollo Meta Server 地址\n  bootstrap:\n    enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。\n    eagerLoad:\n      enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。\n    namespaces: application # 使用的 Apollo 的命名空间，默认为 application。\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-auto-refresh/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-45-apollo-demo-auto-refresh</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!--  引入 Apollo 客户端，内置对 Apollo 的自动化配置 -->\n        <dependency>\n            <groupId>com.ctrip.framework.apollo</groupId>\n            <artifactId>apollo-client</artifactId>\n            <version>1.5.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-auto-refresh/src/main/java/cn/iocoder/springboot/lab45/apollodemo/Application.java",
    "content": "package cn.iocoder.springboot.lab45.apollodemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-auto-refresh/src/main/java/cn/iocoder/springboot/lab45/apollodemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab45.apollodemo.controller;\n\nimport cn.iocoder.springboot.lab45.apollodemo.properties.TestProperties;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Value(\"${test.test}\")\n    private String test;\n\n    @GetMapping(\"/test\")\n    public String test() {\n        return test;\n    }\n\n    @Autowired\n    private TestProperties testProperties;\n\n    @GetMapping(\"/test_properties\")\n    public TestProperties testProperties() {\n        return testProperties;\n    }\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/logger\")\n    public void logger() {\n        logger.debug(\"[logger][测试一下]\");\n    }\n\n}\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-auto-refresh/src/main/java/cn/iocoder/springboot/lab45/apollodemo/listener/LoggingSystemConfigListener.java",
    "content": "package cn.iocoder.springboot.lab45.apollodemo.listener;\n\nimport com.ctrip.framework.apollo.Config;\nimport com.ctrip.framework.apollo.model.ConfigChangeEvent;\nimport com.ctrip.framework.apollo.spring.annotation.ApolloConfig;\nimport com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;\nimport org.springframework.boot.logging.LogLevel;\nimport org.springframework.boot.logging.LoggingSystem;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.Resource;\nimport java.util.Set;\n\n@Component\npublic class LoggingSystemConfigListener {\n\n    /**\n     * 日志配置项的前缀\n     */\n    private static final String LOGGER_TAG = \"logging.level.\";\n\n    @Resource\n    private LoggingSystem loggingSystem;\n\n    @ApolloConfig\n    private Config config;\n\n    @ApolloConfigChangeListener\n    public void onChange(ConfigChangeEvent changeEvent) throws Exception {\n        // 获得 Apollo 所有配置项\n        Set<String> keys = config.getPropertyNames();\n        // 遍历配置集的每个配置项，判断是否是 logging.level 配置项\n        for (String key : keys) {\n            // 如果是 logging.level 配置项，则设置其对应的日志级别\n            if (key.startsWith(LOGGER_TAG)) {\n                // 获得日志级别\n                String strLevel = config.getProperty(key, \"info\");\n                LogLevel level = LogLevel.valueOf(strLevel.toUpperCase());\n                // 设置日志级别到 LoggingSystem 中\n                loggingSystem.setLogLevel(key.replace(LOGGER_TAG, \"\"), level);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-auto-refresh/src/main/java/cn/iocoder/springboot/lab45/apollodemo/properties/TestProperties.java",
    "content": "package cn.iocoder.springboot.lab45.apollodemo.properties;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConfigurationProperties(prefix = \"test\")\npublic class TestProperties {\n\n    /**\n     * 测试属性\n     */\n    private String test;\n\n    public String getTest() {\n        return test;\n    }\n\n    public TestProperties setTest(String test) {\n        this.test = test;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-auto-refresh/src/main/resources/application.yaml",
    "content": "server:\n  port: 7070 # 避免和本地的 Apollo Portal 端口冲突\n\napp:\n  id: demo-application # 使用的 Apollo 的项目（应用）编号\napollo:\n  meta: http://127.0.0.1:8080 # Apollo Meta Server 地址\n  bootstrap:\n    enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。\n    eagerLoad:\n      enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。\n    namespaces: application # 使用的 Apollo 的命名空间，默认为 application。\n\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-jasypt/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-45-apollo-demo-jasypt</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!--  引入 Apollo 客户端，内置对 Apollo 的自动化配置 -->\n        <dependency>\n            <groupId>com.ctrip.framework.apollo</groupId>\n            <artifactId>apollo-client</artifactId>\n            <version>1.5.1</version>\n        </dependency>\n\n        <!-- 实现对 Jasypt 实现自动化配置 -->\n        <dependency>\n            <groupId>com.github.ulisesbocchio</groupId>\n            <artifactId>jasypt-spring-boot-starter</artifactId>\n            <version>3.0.2</version>\n<!--            <version>2.1.2</version>-->\n<!--            <scope>test</scope>-->\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-jasypt/src/main/java/cn/iocoder/springboot/lab45/apollodemo/Application.java",
    "content": "package cn.iocoder.springboot.lab45.apollodemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-jasypt/src/main/java/cn/iocoder/springboot/lab45/apollodemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab45.apollodemo.controller;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Value(\"${spring.application.name}\")\n    private String applicationName;\n\n    @GetMapping(\"/test\")\n    public String test() {\n        return applicationName;\n    }\n\n}\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-jasypt/src/main/resources/application.yaml",
    "content": "server:\n  port: 7070 # 避免和本地的 Apollo Portal 端口冲突\n\napp:\n  id: demo-application-jasypt # 使用的 Apollo 的项目（应用）编号\napollo:\n  meta: http://127.0.0.1:8080 # Apollo Meta Server 地址\n  bootstrap:\n    enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。\n    eagerLoad:\n      enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。\n    namespaces: application # 使用的 Apollo 的命名空间，默认为 application。\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-jasypt/src/test/java/cn/iocoder/springboot/lab45/apollodemo/JasyptTest.java",
    "content": "package cn.iocoder.springboot.lab45.apollodemo;\n\nimport org.jasypt.encryption.StringEncryptor;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class JasyptTest {\n\n    @Autowired\n    private StringEncryptor encryptor;\n\n    @Test\n    public void encode() {\n        // 第一个加密\n        String applicationName = \"demo-application\";\n        System.out.println(encryptor.encrypt(applicationName));\n\n//        // 第二个加密\n//        applicationName = \"demo-app\";\n//        System.out.println(encryptor.encrypt(applicationName));\n    }\n\n    @Value(\"${spring.application.name}\")\n    private String applicationName;\n\n    @Test\n    public void print() {\n        System.out.println(applicationName);\n    }\n\n    @Value(\"${jasypt.encryptor.password}\")\n    private String password;\n\n}\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-multi/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-45-apollo-demo-multi</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!--  引入 Apollo 客户端，内置对 Apollo 的自动化配置 -->\n        <dependency>\n            <groupId>com.ctrip.framework.apollo</groupId>\n            <artifactId>apollo-client</artifactId>\n            <version>1.5.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-multi/src/main/java/cn/iocoder/springboot/lab45/apollodemo/Application.java",
    "content": "package cn.iocoder.springboot.lab45.apollodemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.core.env.Environment;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);\n\n        // 查看 Environment\n        Environment environment = context.getEnvironment();\n        System.out.println(environment);\n    }\n\n}\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-multi/src/main/java/cn/iocoder/springboot/lab45/apollodemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab45.apollodemo.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.core.env.Environment;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private Environment environment;\n\n    @GetMapping(\"/test\")\n    public void test() {\n        System.out.println(environment);\n    }\n\n}\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-multi/src/main/resources/application.yaml",
    "content": "server:\n  port: 7070 # 避免和本地的 Apollo Portal 端口冲突\n\napp:\n  id: demo-application-multi # 使用的 Apollo 的项目（应用）编号\napollo:\n  meta: http://127.0.0.1:8080 # Apollo Meta Server 地址\n  bootstrap:\n    enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。\n    eagerLoad:\n      enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。\n    namespaces: application, db # 使用的 Apollo 的命名空间，默认为 application。\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-profiles/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-45-apollo-demo-profiles</artifactId>\n\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!--  引入 Apollo 客户端，内置对 Apollo 的自动化配置 -->\n        <dependency>\n            <groupId>com.ctrip.framework.apollo</groupId>\n            <artifactId>apollo-client</artifactId>\n            <version>1.5.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-profiles/src/main/java/cn/iocoder/springboot/lab45/apollodemo/ProfilesApplication.java",
    "content": "package cn.iocoder.springboot.lab45.apollodemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProfilesApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProfilesApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-profiles/src/main/resources/application-dev.yaml",
    "content": "app:\n  id: demo-application-profiles # 使用的 Apollo 的项目（应用）编号\napollo:\n  meta: http://127.0.0.1:8080 # Apollo Meta Server 地址\n  bootstrap:\n    enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。\n    eagerLoad:\n      enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。\n    namespaces: application # 使用的 Apollo 的命名空间，默认为 application。\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-profiles/src/main/resources/application-prod.yaml",
    "content": "app:\n  id: demo-application-profiles # 使用的 Apollo 的项目（应用）编号\napollo:\n  meta: http://127.0.0.1:18080 # Apollo Meta Server 地址\n  bootstrap:\n    enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。\n    eagerLoad:\n      enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。\n    namespaces: application # 使用的 Apollo 的命名空间，默认为 application。\n"
  },
  {
    "path": "lab-45/lab-45-apollo-demo-profiles/src/main/resources/application.yaml",
    "content": "#server:\n#  port: 7070\n\nspring:\n  application:\n    name: demo-application\n"
  },
  {
    "path": "lab-45/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-45</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-45-apollo-demo</module>\n        <module>lab-45-apollo-demo-profiles</module>\n        <module>lab-45-apollo-demo-auto-refresh</module>\n        <module>lab-45-apollo-demo-jasypt</module>\n        <module>lab-45-apollo-demo-multi</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-45/《芋道 Spring Boot 配置中心 Apollo 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/config-apollo/?github>\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-46-sentinel-demo</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- Sentinel 核心库 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-core</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 接入控制台 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-transport-simple-http</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 对 SpringMVC 的支持 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-spring-webmvc-adapter</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 对【热点参数限流】的支持 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-parameter-flow-control</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 对 Spring AOP 的拓展 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-annotation-aspectj</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/Application.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        // 设置系统属性 project.name，提供给 Sentinel 读取\n        System.setProperty(\"project.name\", \"demo-application\");\n\n        // 启动 Spring Boot 应用\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SentinelConfiguration.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo.config;\n\nimport com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class SentinelConfiguration {\n\n    @Bean\n    public SentinelResourceAspect sentinelResourceAspect() {\n        return new SentinelResourceAspect();\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SpringMvcConfiguration.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo.config;\n\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor;\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebTotalInterceptor;\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcConfig;\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcTotalConfig;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\npublic class SpringMvcConfiguration implements WebMvcConfigurer {\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        // Add Sentinel interceptor\n//        addSentinelWebTotalInterceptor(registry);\n        addSentinelWebInterceptor(registry);\n    }\n\n    private void addSentinelWebInterceptor(InterceptorRegistry registry) {\n        // 创建 SentinelWebMvcConfig 对象\n        SentinelWebMvcConfig config = new SentinelWebMvcConfig();\n        config.setHttpMethodSpecify(true); // 是否包含请求方法。即基于 URL 创建的资源，是否包含 Method。\n        // config.setBlockExceptionHandler(new DefaultBlockExceptionHandler()); // 设置 BlockException 处理器。\n//        config.setOriginParser(new RequestOriginParser() { // 设置请求来源解析器。用于黑白名单控制功能。\n//\n//            @Override\n//            public String parseOrigin(HttpServletRequest request) {\n//                // 从 Header 中，获得请求来源\n//                String origin = request.getHeader(\"s-user\");\n//                // 如果为空，给一个默认的\n//                if (StringUtils.isEmpty(origin)) {\n//                    origin = \"default\";\n//                }\n//                return origin;\n//            }\n//\n//        });\n\n        // 添加 SentinelWebInterceptor 拦截器\n        registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns(\"/**\");\n    }\n\n    private void addSentinelWebTotalInterceptor(InterceptorRegistry registry) {\n        // 创建 SentinelWebMvcTotalConfig 对象\n        SentinelWebMvcTotalConfig config = new SentinelWebMvcTotalConfig();\n\n        // 添加 SentinelWebTotalInterceptor 拦截器\n        registry.addInterceptor(new SentinelWebTotalInterceptor(config)).addPathPatterns(\"/**\");\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo.controller;\n\nimport com.alibaba.csp.sentinel.Entry;\nimport com.alibaba.csp.sentinel.SphU;\nimport com.alibaba.csp.sentinel.annotation.SentinelResource;\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return \"echo\";\n    }\n\n    @GetMapping(\"/test\")\n    public String test() {\n        return \"test\";\n    }\n\n    @GetMapping(\"/sleep\")\n    public String sleep() throws InterruptedException {\n        Thread.sleep(100L);\n        return \"sleep\";\n    }\n\n    // 测试热点参数限流\n    @GetMapping(\"/product_info\")\n    @SentinelResource(\"demo_product_info_hot\")\n    public String productInfo(Integer id) {\n        return \"商品编号：\" + id;\n    }\n\n    // 手动使用 Sentinel 客户端 API\n    @GetMapping(\"/entry_demo\")\n    public String entryDemo() {\n        Entry entry = null;\n        try {\n            // 访问资源\n            entry = SphU.entry(\"entry_demo\");\n\n            // ... 执行业务逻辑\n\n            return \"执行成功\";\n        } catch (BlockException ex) {\n            return \"被拒绝\";\n        } finally {\n            // 释放资源\n            if (entry != null) {\n                entry.exit();\n            }\n        }\n    }\n\n    // 测试 @SentinelResource 注解\n    @GetMapping(\"/annotations_demo\")\n    @SentinelResource(value = \"annotations_demo_resource\",\n            blockHandler = \"blockHandler\",\n            fallback = \"fallback\")\n    public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException {\n        if (id == null) {\n            throw new IllegalArgumentException(\"id 参数不允许为空\");\n        }\n        return \"success...\";\n    }\n\n    // BlockHandler 处理函数，参数最后多一个 BlockException，其余与原函数一致.\n    public String blockHandler(Integer id, BlockException ex) {\n        return \"block：\" + ex.getClass().getSimpleName();\n    }\n\n    // Fallback 处理函数，函数签名与原函数一致或加一个 Throwable 类型的参数.\n    public String fallback(Integer id, Throwable throwable) {\n        return \"fallback：\" + throwable.getMessage();\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/web/GlobalExceptionHandler.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo.web;\n\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n@ControllerAdvice(basePackages = \"cn.iocoder.springboot.lab46.sentineldemo.controller\")\npublic class GlobalExceptionHandler {\n\n    @ResponseBody\n    @ExceptionHandler(value = BlockException.class)\n    public String blockExceptionHandler(BlockException blockException) {\n        return \"请求过于频繁\";\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo/src/main/resources/sentinel.properties",
    "content": "csp.sentinel.dashboard.server=127.0.0.1:7070\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-apollo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-46-sentinel-demo-apollo</artifactId>\n\n    <dependencies>\n\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- Sentinel 核心库 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-core</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 接入控制台 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-transport-simple-http</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 对 SpringMVC 的支持 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-spring-webmvc-adapter</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 对【热点参数限流】的支持 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-parameter-flow-control</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 对 Spring AOP 的拓展 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-annotation-aspectj</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 对 Apollo 作为数据源的支持 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-datasource-apollo</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-apollo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/Application.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        // 设置系统属性 project.name，提供给 Sentinel 读取\n        System.setProperty(\"project.name\", \"demo-application\");\n\n        // 启动 Spring Boot 应用\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-apollo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SentinelConfiguration.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo.config;\n\nimport com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.csp.sentinel.datasource.apollo.ApolloDataSource;\nimport com.alibaba.csp.sentinel.slots.block.flow.FlowRule;\nimport com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.util.Arrays;\nimport java.util.List;\n\n@Configuration\npublic class SentinelConfiguration {\n\n    @Value(\"${spring.application.name}\")\n    private String applicationName;\n\n    @Bean\n    public SentinelResourceAspect sentinelResourceAspect() {\n        return new SentinelResourceAspect();\n    }\n\n    @Bean\n    public ApolloDataSource apolloDataSource(ObjectMapper objectMapper) {\n        // Apollo 配置。这里先写死，推荐后面写到 application.yaml 配置文件中。\n        String appId = applicationName; // Apollo 项目编号。一般情况下，推荐和 spring.application.name 保持一致\n        String serverAddress = \"http://localhost:8080\"; // Apollo Meta 服务器地址\n        String namespace = \"application\"; // Apollo 命名空间\n        String flowRuleKey = \"sentinel.flow-rule\"; // Apollo 配置项的 KEY\n\n        // 创建 ApolloDataSource 对象\n        System.setProperty(\"app.id\", appId);\n        System.setProperty(\"apollo.meta\", serverAddress);\n        ApolloDataSource<List<FlowRule>> apolloDataSource = new ApolloDataSource<>(namespace, flowRuleKey, \"\",\n                new Converter<String, List<FlowRule>>() { // 转换器，将读取的 Apollo 配置，转换成 FlowRule 数组\n                    @Override\n                    public List<FlowRule> convert(String value) {\n                        try {\n                            return Arrays.asList(objectMapper.readValue(value, FlowRule[].class));\n                        } catch (JsonProcessingException e) {\n                            throw new RuntimeException(e);\n                        }\n                    }\n                });\n\n        // 注册到 FlowRuleManager 中\n        FlowRuleManager.register2Property(apolloDataSource.getProperty());\n        return apolloDataSource;\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-apollo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SpringMvcConfiguration.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo.config;\n\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor;\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebTotalInterceptor;\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcConfig;\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcTotalConfig;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\npublic class SpringMvcConfiguration implements WebMvcConfigurer {\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        // Add Sentinel interceptor\n//        addSentinelWebTotalInterceptor(registry);\n        addSentinelWebInterceptor(registry);\n    }\n\n    private void addSentinelWebInterceptor(InterceptorRegistry registry) {\n        // 创建 SentinelWebMvcConfig 对象\n        SentinelWebMvcConfig config = new SentinelWebMvcConfig();\n        config.setHttpMethodSpecify(true); // 是否包含请求方法。即基于 URL 创建的资源，是否包含 Method。\n        // config.setBlockExceptionHandler(new DefaultBlockExceptionHandler()); // 设置 BlockException 处理器。\n//        config.setOriginParser(new RequestOriginParser() { // 设置请求来源解析器。用于黑白名单控制功能。\n//\n//            @Override\n//            public String parseOrigin(HttpServletRequest request) {\n//                // 从 Header 中，获得请求来源\n//                String origin = request.getHeader(\"s-user\");\n//                // 如果为空，给一个默认的\n//                if (StringUtils.isEmpty(origin)) {\n//                    origin = \"default\";\n//                }\n//                return origin;\n//            }\n//\n//        });\n\n        // 添加 SentinelWebInterceptor 拦截器\n        registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns(\"/**\");\n    }\n\n    private void addSentinelWebTotalInterceptor(InterceptorRegistry registry) {\n        // 创建 SentinelWebMvcTotalConfig 对象\n        SentinelWebMvcTotalConfig config = new SentinelWebMvcTotalConfig();\n\n        // 添加 SentinelWebTotalInterceptor 拦截器\n        registry.addInterceptor(new SentinelWebTotalInterceptor(config)).addPathPatterns(\"/**\");\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-apollo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo.controller;\n\nimport com.alibaba.csp.sentinel.Entry;\nimport com.alibaba.csp.sentinel.SphU;\nimport com.alibaba.csp.sentinel.annotation.SentinelResource;\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return \"echo\";\n    }\n\n    @GetMapping(\"/test\")\n    public String test() {\n        return \"test\";\n    }\n\n    @GetMapping(\"/sleep\")\n    public String sleep() throws InterruptedException {\n        Thread.sleep(100L);\n        return \"sleep\";\n    }\n\n    // 测试热点参数限流\n    @GetMapping(\"/product_info\")\n    @SentinelResource(\"demo_product_info_hot\")\n    public String productInfo(Integer id) {\n        return \"商品编号：\" + id;\n    }\n\n    // 手动使用 Sentinel 客户端 API\n    @GetMapping(\"/entry_demo\")\n    public String entryDemo() {\n        Entry entry = null;\n        try {\n            // 访问资源\n            entry = SphU.entry(\"entry_demo\");\n\n            // ... 执行业务逻辑\n\n            return \"执行成功\";\n        } catch (BlockException ex) {\n            return \"被拒绝\";\n        } finally {\n            // 释放资源\n            if (entry != null) {\n                entry.exit();\n            }\n        }\n    }\n\n    // 测试 @SentinelResource 注解\n    @GetMapping(\"/annotations_demo\")\n    @SentinelResource(value = \"annotations_demo_resource\",\n            blockHandler = \"blockHandler\",\n            fallback = \"fallback\")\n    public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException {\n        if (id == null) {\n            throw new IllegalArgumentException(\"id 参数不允许为空\");\n        }\n        return \"success...\";\n    }\n\n    // BlockHandler 处理函数，参数最后多一个 BlockException，其余与原函数一致.\n    public String blockHandler(Integer id, BlockException ex) {\n        return \"block：\" + ex.getClass().getSimpleName();\n    }\n\n    // Fallback 处理函数，函数签名与原函数一致或加一个 Throwable 类型的参数.\n    public String fallback(Integer id, Throwable throwable) {\n        return \"fallback：\" + throwable.getMessage();\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-apollo/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/web/GlobalExceptionHandler.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo.web;\n\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n@ControllerAdvice(basePackages = \"cn.iocoder.springboot.lab46.sentineldemo.controller\")\npublic class GlobalExceptionHandler {\n\n    @ResponseBody\n    @ExceptionHandler(value = BlockException.class)\n    public String blockExceptionHandler(BlockException blockException) {\n        return \"请求过于频繁\";\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-apollo/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application\n\nserver:\n  port: 18080 # 避免和 Apollo 使用到的 8080 端口冲突\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-apollo/src/main/resources/sentinel.properties",
    "content": "csp.sentinel.dashboard.server=127.0.0.1:7070\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-file/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-46-sentinel-demo-file</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- Sentinel 核心库 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-core</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 接入控制台 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-transport-simple-http</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 对 SpringMVC 的支持 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-spring-webmvc-adapter</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 对【热点参数限流】的支持 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-parameter-flow-control</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 对 Spring AOP 的拓展 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-annotation-aspectj</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 对数据源的拓展的基础库，内置了 File 数据源 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-datasource-extension</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/Application.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        // 设置系统属性 project.name，提供给 Sentinel 读取\n        System.setProperty(\"project.name\", \"demo-application\");\n\n        // 启动 Spring Boot 应用\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SentinelConfiguration.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo.config;\n\nimport com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.csp.sentinel.datasource.FileRefreshableDataSource;\nimport com.alibaba.csp.sentinel.datasource.FileWritableDataSource;\nimport com.alibaba.csp.sentinel.slots.block.flow.FlowRule;\nimport com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;\nimport com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.io.ClassPathResource;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.List;\n\n@Configuration\npublic class SentinelConfiguration {\n\n    @Bean\n    public SentinelResourceAspect sentinelResourceAspect() {\n        return new SentinelResourceAspect();\n    }\n\n//    @Bean\n    public FileRefreshableDataSource<List<FlowRule>> refreshableDataSource(ObjectMapper objectMapper) throws IOException {\n        // File 配置。这里先写死，推荐后面写到 application.yaml 配置文件中。\n        ClassPathResource resource = new ClassPathResource(\"/flow-rule.json\");\n\n        // 创建 FileRefreshableDataSource 对象\n        FileRefreshableDataSource<List<FlowRule>> refreshableDataSource = new FileRefreshableDataSource<>(resource.getFile(),\n                new Converter<String, List<FlowRule>>() { // 转换器，将读取的文本配置，转换成 FlowRule 数组\n                    @Override\n                    public List<FlowRule> convert(String value) {\n                        try {\n                            return Arrays.asList(objectMapper.readValue(value, FlowRule[].class));\n                        } catch (JsonProcessingException e) {\n                            throw new RuntimeException(e);\n                        }\n                    }\n                });\n\n        // 注册到 FlowRuleManager 中\n        FlowRuleManager.register2Property(refreshableDataSource.getProperty());\n        return refreshableDataSource;\n    }\n\n    @Bean\n    public FileWritableDataSource writableDataSource(ObjectMapper objectMapper) throws IOException {\n        // File 配置。这里先写死，推荐后面写到 application.yaml 配置文件中。\n        String directory = System.getProperty(\"user.home\") + File.separator\n                + \"sentinel\" + File.separator\n                + System.getProperty(\"project.name\");\n        mkdirs(directory);\n        String path = directory + File.separator + \"flow-rule.json\";\n        creteFile(path);\n\n        // 创建 FileRefreshableDataSource 对象\n        FileRefreshableDataSource<List<FlowRule>> refreshableDataSource = new FileRefreshableDataSource<>(path,\n                new Converter<String, List<FlowRule>>() { // 转换器，将读取的文本配置，转换成 FlowRule 数组\n                    @Override\n                    public List<FlowRule> convert(String value) {\n                        try {\n                            return Arrays.asList(objectMapper.readValue(value, FlowRule[].class));\n                        } catch (JsonProcessingException e) {\n                            throw new RuntimeException(e);\n                        }\n                    }\n                });\n        // 注册到 FlowRuleManager 中\n        FlowRuleManager.register2Property(refreshableDataSource.getProperty());\n\n        // 创建 FileWritableDataSource 对象\n        FileWritableDataSource<List<FlowRule>> fileWritableDataSource = new FileWritableDataSource<>(path,\n                new Converter<List<FlowRule>, String>() {\n                    @Override\n                    public String convert(List<FlowRule> source) {\n                        try {\n                            return objectMapper.writeValueAsString(source);\n                        } catch (JsonProcessingException e) {\n                            throw new RuntimeException(e);\n                        }\n                    }\n                });\n\n        // 注册到 WritableDataSourceRegistry 中\n        WritableDataSourceRegistry.registerFlowDataSource(fileWritableDataSource);\n        return fileWritableDataSource;\n    }\n\n    private void mkdirs(String path) {\n        File file = new File(path);\n        if (file.exists()) {\n            return;\n        }\n        file.mkdirs();\n    }\n\n    private void creteFile(String path) throws IOException {\n        File file = new File(path);\n        if (file.exists()) {\n            return;\n        }\n        file.createNewFile();\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SpringMvcConfiguration.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo.config;\n\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor;\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebTotalInterceptor;\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcConfig;\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcTotalConfig;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\npublic class SpringMvcConfiguration implements WebMvcConfigurer {\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        // Add Sentinel interceptor\n//        addSentinelWebTotalInterceptor(registry);\n        addSentinelWebInterceptor(registry);\n    }\n\n    private void addSentinelWebInterceptor(InterceptorRegistry registry) {\n        // 创建 SentinelWebMvcConfig 对象\n        SentinelWebMvcConfig config = new SentinelWebMvcConfig();\n        config.setHttpMethodSpecify(true); // 是否包含请求方法。即基于 URL 创建的资源，是否包含 Method。\n        // config.setBlockExceptionHandler(new DefaultBlockExceptionHandler()); // 设置 BlockException 处理器。\n//        config.setOriginParser(new RequestOriginParser() { // 设置请求来源解析器。用于黑白名单控制功能。\n//\n//            @Override\n//            public String parseOrigin(HttpServletRequest request) {\n//                // 从 Header 中，获得请求来源\n//                String origin = request.getHeader(\"s-user\");\n//                // 如果为空，给一个默认的\n//                if (StringUtils.isEmpty(origin)) {\n//                    origin = \"default\";\n//                }\n//                return origin;\n//            }\n//\n//        });\n\n        // 添加 SentinelWebInterceptor 拦截器\n        registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns(\"/**\");\n    }\n\n    private void addSentinelWebTotalInterceptor(InterceptorRegistry registry) {\n        // 创建 SentinelWebMvcTotalConfig 对象\n        SentinelWebMvcTotalConfig config = new SentinelWebMvcTotalConfig();\n\n        // 添加 SentinelWebTotalInterceptor 拦截器\n        registry.addInterceptor(new SentinelWebTotalInterceptor(config)).addPathPatterns(\"/**\");\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo.controller;\n\nimport com.alibaba.csp.sentinel.Entry;\nimport com.alibaba.csp.sentinel.SphU;\nimport com.alibaba.csp.sentinel.annotation.SentinelResource;\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return \"echo\";\n    }\n\n    @GetMapping(\"/test\")\n    public String test() {\n        return \"test\";\n    }\n\n    @GetMapping(\"/sleep\")\n    public String sleep() throws InterruptedException {\n        Thread.sleep(100L);\n        return \"sleep\";\n    }\n\n    // 测试热点参数限流\n    @GetMapping(\"/product_info\")\n    @SentinelResource(\"demo_product_info_hot\")\n    public String productInfo(Integer id) {\n        return \"商品编号：\" + id;\n    }\n\n    // 手动使用 Sentinel 客户端 API\n    @GetMapping(\"/entry_demo\")\n    public String entryDemo() {\n        Entry entry = null;\n        try {\n            // 访问资源\n            entry = SphU.entry(\"entry_demo\");\n\n            // ... 执行业务逻辑\n\n            return \"执行成功\";\n        } catch (BlockException ex) {\n            return \"被拒绝\";\n        } finally {\n            // 释放资源\n            if (entry != null) {\n                entry.exit();\n            }\n        }\n    }\n\n    // 测试 @SentinelResource 注解\n    @GetMapping(\"/annotations_demo\")\n    @SentinelResource(value = \"annotations_demo_resource\",\n            blockHandler = \"blockHandler\",\n            fallback = \"fallback\")\n    public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException {\n        if (id == null) {\n            throw new IllegalArgumentException(\"id 参数不允许为空\");\n        }\n        return \"success...\";\n    }\n\n    // BlockHandler 处理函数，参数最后多一个 BlockException，其余与原函数一致.\n    public String blockHandler(Integer id, BlockException ex) {\n        return \"block：\" + ex.getClass().getSimpleName();\n    }\n\n    // Fallback 处理函数，函数签名与原函数一致或加一个 Throwable 类型的参数.\n    public String fallback(Integer id, Throwable throwable) {\n        return \"fallback：\" + throwable.getMessage();\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-file/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/web/GlobalExceptionHandler.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo.web;\n\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n@ControllerAdvice(basePackages = \"cn.iocoder.springboot.lab46.sentineldemo.controller\")\npublic class GlobalExceptionHandler {\n\n    @ResponseBody\n    @ExceptionHandler(value = BlockException.class)\n    public String blockExceptionHandler(BlockException blockException) {\n        return \"请求过于频繁\";\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-file/src/main/resources/flow-rule.json",
    "content": "[\n  {\n    \"resource\": \"GET:/demo/echo\",\n    \"limitApp\": \"default\",\n    \"grade\": 1,\n    \"count\": 5,\n    \"strategy\": 0,\n    \"controlBehavior\": 0,\n    \"clusterMode\": false\n  }\n]\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-file/src/main/resources/sentinel.properties",
    "content": "csp.sentinel.dashboard.server=127.0.0.1:7070\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-nacos/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-46-sentinel-demo-nacos</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- Sentinel 核心库 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-core</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 接入控制台 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-transport-simple-http</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 对 SpringMVC 的支持 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-spring-webmvc-adapter</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 对【热点参数限流】的支持 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-parameter-flow-control</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 对 Spring AOP 的拓展 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-annotation-aspectj</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n        <!-- Sentinel 对 Nacos 作为数据源的支持 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-datasource-nacos</artifactId>\n            <version>1.7.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-nacos/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/Application.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        // 设置系统属性 project.name，提供给 Sentinel 读取\n        System.setProperty(\"project.name\", \"demo-application\");\n\n        // 启动 Spring Boot 应用\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-nacos/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SentinelConfiguration.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo.config;\n\nimport com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;\nimport com.alibaba.csp.sentinel.datasource.Converter;\nimport com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;\nimport com.alibaba.csp.sentinel.slots.block.flow.FlowRule;\nimport com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;\nimport com.alibaba.nacos.api.PropertyKeyConst;\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.Properties;\n\n@Configuration\npublic class SentinelConfiguration {\n\n    @Bean\n    public SentinelResourceAspect sentinelResourceAspect() {\n        return new SentinelResourceAspect();\n    }\n\n    @Bean\n    public NacosDataSource nacosDataSource(ObjectMapper objectMapper) {\n        // Nacos 配置。这里先写死，推荐后面写到 application.yaml 配置文件中。\n        String serverAddress = \"127.0.0.1:8848\"; // Nacos 服务器地址\n        String namespace = \"\"; // Nacos 命名空间\n        String dataId = \"demo-application-flow-rule\"; // Nacos 配置集编号\n//        String dataId = \"example-sentinel\"; // Nacos 配置集编号\n        String group = \"DEFAULT_GROUP\"; // Nacos 配置分组\n\n        // 创建 NacosDataSource 对象\n        Properties properties = new Properties();\n        properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverAddress);\n        properties.setProperty(PropertyKeyConst.NAMESPACE, namespace);\n        NacosDataSource<List<FlowRule>> nacosDataSource = new NacosDataSource<>(properties, group, dataId,\n                new Converter<String, List<FlowRule>>() { // 转换器，将读取的 Nacos 配置，转换成 FlowRule 数组\n                    @Override\n                    public List<FlowRule> convert(String value) {\n                        try {\n                            return Arrays.asList(objectMapper.readValue(value, FlowRule[].class));\n                        } catch (JsonProcessingException e) {\n                            throw new RuntimeException(e);\n                        }\n                    }\n                });\n\n        // 注册到 FlowRuleManager 中\n        FlowRuleManager.register2Property(nacosDataSource.getProperty());\n        return nacosDataSource;\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-nacos/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/config/SpringMvcConfiguration.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo.config;\n\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebInterceptor;\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.SentinelWebTotalInterceptor;\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcConfig;\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.config.SentinelWebMvcTotalConfig;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.servlet.config.annotation.InterceptorRegistry;\nimport org.springframework.web.servlet.config.annotation.WebMvcConfigurer;\n\n@Configuration\npublic class SpringMvcConfiguration implements WebMvcConfigurer {\n\n    @Override\n    public void addInterceptors(InterceptorRegistry registry) {\n        // Add Sentinel interceptor\n//        addSentinelWebTotalInterceptor(registry);\n        addSentinelWebInterceptor(registry);\n    }\n\n    private void addSentinelWebInterceptor(InterceptorRegistry registry) {\n        // 创建 SentinelWebMvcConfig 对象\n        SentinelWebMvcConfig config = new SentinelWebMvcConfig();\n        config.setHttpMethodSpecify(true); // 是否包含请求方法。即基于 URL 创建的资源，是否包含 Method。\n        // config.setBlockExceptionHandler(new DefaultBlockExceptionHandler()); // 设置 BlockException 处理器。\n//        config.setOriginParser(new RequestOriginParser() { // 设置请求来源解析器。用于黑白名单控制功能。\n//\n//            @Override\n//            public String parseOrigin(HttpServletRequest request) {\n//                // 从 Header 中，获得请求来源\n//                String origin = request.getHeader(\"s-user\");\n//                // 如果为空，给一个默认的\n//                if (StringUtils.isEmpty(origin)) {\n//                    origin = \"default\";\n//                }\n//                return origin;\n//            }\n//\n//        });\n\n        // 添加 SentinelWebInterceptor 拦截器\n        registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns(\"/**\");\n    }\n\n    private void addSentinelWebTotalInterceptor(InterceptorRegistry registry) {\n        // 创建 SentinelWebMvcTotalConfig 对象\n        SentinelWebMvcTotalConfig config = new SentinelWebMvcTotalConfig();\n\n        // 添加 SentinelWebTotalInterceptor 拦截器\n        registry.addInterceptor(new SentinelWebTotalInterceptor(config)).addPathPatterns(\"/**\");\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-nacos/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo.controller;\n\nimport com.alibaba.csp.sentinel.Entry;\nimport com.alibaba.csp.sentinel.SphU;\nimport com.alibaba.csp.sentinel.annotation.SentinelResource;\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return \"echo\";\n    }\n\n    @GetMapping(\"/test\")\n    public String test() {\n        return \"test\";\n    }\n\n    @GetMapping(\"/sleep\")\n    public String sleep() throws InterruptedException {\n        Thread.sleep(100L);\n        return \"sleep\";\n    }\n\n    // 测试热点参数限流\n    @GetMapping(\"/product_info\")\n    @SentinelResource(\"demo_product_info_hot\")\n    public String productInfo(Integer id) {\n        return \"商品编号：\" + id;\n    }\n\n    // 手动使用 Sentinel 客户端 API\n    @GetMapping(\"/entry_demo\")\n    public String entryDemo() {\n        Entry entry = null;\n        try {\n            // 访问资源\n            entry = SphU.entry(\"entry_demo\");\n\n            // ... 执行业务逻辑\n\n            return \"执行成功\";\n        } catch (BlockException ex) {\n            return \"被拒绝\";\n        } finally {\n            // 释放资源\n            if (entry != null) {\n                entry.exit();\n            }\n        }\n    }\n\n    // 测试 @SentinelResource 注解\n    @GetMapping(\"/annotations_demo\")\n    @SentinelResource(value = \"annotations_demo_resource\",\n            blockHandler = \"blockHandler\",\n            fallback = \"fallback\")\n    public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException {\n        if (id == null) {\n            throw new IllegalArgumentException(\"id 参数不允许为空\");\n        }\n        return \"success...\";\n    }\n\n    // BlockHandler 处理函数，参数最后多一个 BlockException，其余与原函数一致.\n    public String blockHandler(Integer id, BlockException ex) {\n        return \"block：\" + ex.getClass().getSimpleName();\n    }\n\n    // Fallback 处理函数，函数签名与原函数一致或加一个 Throwable 类型的参数.\n    public String fallback(Integer id, Throwable throwable) {\n        return \"fallback：\" + throwable.getMessage();\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-nacos/src/main/java/cn/iocoder/springboot/lab46/sentineldemo/web/GlobalExceptionHandler.java",
    "content": "package cn.iocoder.springboot.lab46.sentineldemo.web;\n\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n@ControllerAdvice(basePackages = \"cn.iocoder.springboot.lab46.sentineldemo.controller\")\npublic class GlobalExceptionHandler {\n\n    @ResponseBody\n    @ExceptionHandler(value = BlockException.class)\n    public String blockExceptionHandler(BlockException blockException) {\n        return \"请求过于频繁\";\n    }\n\n}\n"
  },
  {
    "path": "lab-46/lab-46-sentinel-demo-nacos/src/main/resources/sentinel.properties",
    "content": "csp.sentinel.dashboard.server=127.0.0.1:7070\n"
  },
  {
    "path": "lab-46/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-46</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-46-sentinel-demo</module>\n        <module>lab-46-sentinel-demo-nacos</module>\n        <module>lab-46-sentinel-demo-apollo</module>\n        <module>lab-46-sentinel-demo-file</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-46/《芋道 Spring Boot 服务容错 Sentinel 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Sentinel/?github>\n"
  },
  {
    "path": "lab-47/lab-47-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-47</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-47-demo</artifactId>\n\n    <dependencies>\n        <!-- 引入自定义 Starter -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>yunai-server-spring-boot-starter</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-47/lab-47-demo/src/main/java/cn/iocoder/springboot/lab47/demo/DemoApplication.java",
    "content": "package cn.iocoder.springboot.lab47.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-47/lab-47-demo/src/main/resources/application.yaml",
    "content": "yunai:\n  server:\n    port: 8888 # 自定义 HttpServer 端口\n"
  },
  {
    "path": "lab-47/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-47</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>yunai-server-spring-boot-starter</module>\n        <module>lab-47-demo</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-47/yunai-server-spring-boot-starter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-47</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>yunai-server-spring-boot-starter</artifactId>\n\n    <dependencies>\n        <!-- 引入 Spring Boot Starter 基础库 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n            <version>2.2.2.RELEASE</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-47/yunai-server-spring-boot-starter/src/main/java/cn/iocoder/springboot/lab47/yunaiserver/autoconfigure/YunaiServerAutoConfiguration.java",
    "content": "package cn.iocoder.springboot.lab47.yunaiserver.autoconfigure;\n\nimport com.sun.net.httpserver.HttpServer;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.boot.autoconfigure.condition.ConditionalOnClass;\nimport org.springframework.boot.context.properties.EnableConfigurationProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.io.IOException;\nimport java.net.InetSocketAddress;\n\n@Configuration // 声明配置类\n@EnableConfigurationProperties(YunaiServerProperties.class) // 使 YunaiServerProperties 配置属性类生效\npublic class YunaiServerAutoConfiguration {\n\n    private Logger logger = LoggerFactory.getLogger(YunaiServerAutoConfiguration.class);\n\n    @Bean // 声明创建 Bean\n    @ConditionalOnClass(HttpServer.class) // 需要项目中存在 com.sun.net.httpserver.HttpServer 类。该类为 JDK 自带，所以一定成立。\n    public HttpServer httpServer(YunaiServerProperties serverProperties) throws IOException {\n        // 创建 HttpServer 对象，并启动\n        HttpServer server = HttpServer.create(new InetSocketAddress(serverProperties.getPort()), 0);\n        server.start();\n        logger.info(\"[httpServer][启动服务器成功，端口为:{}]\", serverProperties.getPort());\n\n        // 返回\n        return server;\n    }\n\n}\n"
  },
  {
    "path": "lab-47/yunai-server-spring-boot-starter/src/main/java/cn/iocoder/springboot/lab47/yunaiserver/autoconfigure/YunaiServerProperties.java",
    "content": "package cn.iocoder.springboot.lab47.yunaiserver.autoconfigure;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\n\n@ConfigurationProperties(prefix = \"yunai.server\")\npublic class YunaiServerProperties {\n\n    /**\n     * 默认端口\n     */\n    private static final Integer DEFAULT_PORT = 8000;\n\n    /**\n     * 端口\n     */\n    private Integer port = DEFAULT_PORT;\n\n    public static Integer getDefaultPort() {\n        return DEFAULT_PORT;\n    }\n\n    public Integer getPort() {\n        return port;\n    }\n\n    public YunaiServerProperties setPort(Integer port) {\n        this.port = port;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-47/yunai-server-spring-boot-starter/src/main/resources/META-INF/spring.factories",
    "content": "org.springframework.boot.autoconfigure.EnableAutoConfiguration=\\\ncn.iocoder.springboot.lab47.yunaiserver.autoconfigure.YunaiServerAutoConfiguration\n"
  },
  {
    "path": "lab-47/《芋道 Spring Boot 自动配置原理》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/autoconfigure/?github>\n"
  },
  {
    "path": "lab-48-hot-swap/lab-48-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-48-demo</artifactId>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-48-hot-swap/lab-48-demo/src/main/java/cn/iocoder/springboot/lab48/demo/DemoApplication.java",
    "content": "package cn.iocoder.springboot.lab48.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-48-hot-swap/lab-48-demo/src/main/java/cn/iocoder/springboot/lab48/demo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab48.demo.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return \"echo\";\n    }\n\n}\n"
  },
  {
    "path": "lab-48-hot-swap/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-48-hot-swap</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>lab-48-demo</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-48-hot-swap/《芋道 Spring Boot 热部署入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/hot-swap/?github>\n"
  },
  {
    "path": "lab-49/lab-49-lombok-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-49-lombok-demo</artifactId>\n\n    <dependencies>\n        <!-- 引入 Spring Boot Starter 基础库 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 引入 Lombok 依赖 -->\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n<!--            <optional>true</optional>-->\n        </dependency>\n\n        <!-- 实现对 Test 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-49/lab-49-lombok-demo/src/main/java/cn/iocoder/springboot/lab49/lombokdemo/LombokApplication.java",
    "content": "package cn.iocoder.springboot.lab49.lombokdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class LombokApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(LombokApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-49/lab-49-lombok-demo/src/main/java/cn/iocoder/springboot/lab49/lombokdemo/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab49.lombokdemo.dataobject;\n\nimport lombok.Getter;\nimport lombok.Setter;\n\n/**\n * 用户 DO\n */\n@Setter\n@Getter\npublic class UserDO {\n\n    private String username;\n    private String password;\n\n}\n"
  },
  {
    "path": "lab-49/lab-49-lombok-demo/src/main/java/cn/iocoder/springboot/lab49/lombokdemo/dataobject/UserDO01.java",
    "content": "package cn.iocoder.springboot.lab49.lombokdemo.dataobject;\n\nimport lombok.Data;\n\n/**\n * 用户 DO\n */\n@Data\npublic class UserDO01 {\n\n    private String username;\n    private String password;\n\n}\n"
  },
  {
    "path": "lab-49/lab-49-lombok-demo/src/main/java/cn/iocoder/springboot/lab49/lombokdemo/service/UserService.java",
    "content": "package cn.iocoder.springboot.lab49.lombokdemo.service;\n\nimport lombok.extern.slf4j.Slf4j;\n\n@Slf4j\npublic class UserService {\n\n    public static void staticMethod() {\n        log.info(\"静态方法示例\");\n    }\n\n    public void normalMethod() {\n        log.info(\"普通方法示例\");\n    }\n\n}\n"
  },
  {
    "path": "lab-49/lab-49-lombok-demo/src/main/java/cn/iocoder/springboot/lab49/lombokdemo/service/UserService01.java",
    "content": "package cn.iocoder.springboot.lab49.lombokdemo.service;\n\nimport lombok.NonNull;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class UserService01 {\n\n    public void test(@NonNull String username, String password) {\n\n    }\n\n}\n"
  },
  {
    "path": "lab-49/lab-49-lombok-demo/src/test/java/cn/iocoder/springboot/lab49/lombokdemo/dataobject/UserDOTest.java",
    "content": "package cn.iocoder.springboot.lab49.lombokdemo.dataobject;\n\nimport org.junit.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\n\n@SpringBootTest\npublic class UserDOTest {\n\n    @Test\n    public void demo01() {\n        UserDO user = new UserDO();\n        user.setUsername(\"username:1\");\n        user.setPassword(\"password:1\");\n        System.out.println(user);\n    }\n\n}\n"
  },
  {
    "path": "lab-49/lab-49-lombok-demo/src/test/java/cn/iocoder/springboot/lab49/lombokdemo/package-info.java",
    "content": "package cn.iocoder.springboot.lab49.lombokdemo;\n"
  },
  {
    "path": "lab-49/lab-49-lombok-demo/src/test/java/cn/iocoder/springboot/lab49/lombokdemo/service/UserService01Test.java",
    "content": "package cn.iocoder.springboot.lab49.lombokdemo.service;\n\nimport org.junit.Test;\nimport org.springframework.boot.test.context.SpringBootTest;\n\n@SpringBootTest\npublic class UserService01Test {\n\n    private UserService01 userService01 = new UserService01();\n\n    @Test\n    public void test() {\n        userService01.test(null, null);\n    }\n\n}\n"
  },
  {
    "path": "lab-49/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-49</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-49-lombok-demo</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-49/《芋道 Spring Boot 消除冗余代码 Lombok 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Lombok/?github>\n"
  },
  {
    "path": "lab-50/lab-50-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-50-demo</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Java Mail 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-mail</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-50/lab-50-demo/src/main/java/cn/iocoder/springboot/lab50/maildemo/Application.java",
    "content": "package cn.iocoder.springboot.lab50.maildemo;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n}\n"
  },
  {
    "path": "lab-50/lab-50-demo/src/main/resources/application.yml",
    "content": "#spring:\n#  mail: # 配置发送告警的邮箱\n#    host: smtp.163.com\n#    port: 465\n#    protocol: smtps\n#    username: yudaoyuanma@163.com\n#    password: '***'\n#    default-encoding: UTF-8\n##    properties:\n##      smtp:\n##        auth: true\n##        starttls:\n##          enable: true\n##          required: true\n\n\nspring:\n  mail: # 配置发送告警的邮箱\n    host: smtp.exmail.qq.com\n    port: 465\n    protocol: smtps\n    username: yunai.wang@medcloud.cn\n    password: '***'\n    default-encoding: UTF-8\n    properties:\n      smtp:\n        auth: true\n        starttls:\n          enable: true\n          required: true\n"
  },
  {
    "path": "lab-50/lab-50-demo/src/test/java/cn/iocoder/springboot/lab50/maildemo/ApplicationTests.java",
    "content": "package cn.iocoder.springboot.lab50.maildemo;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.mail.SimpleMailMessage;\nimport org.springframework.mail.javamail.JavaMailSender;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class ApplicationTests {\n\n    @Autowired\n    private JavaMailSender mailSender;\n\n    @Value(\"${spring.mail.username}\")\n    private String username;\n\n    @Test\n    public void testSend() {\n        SimpleMailMessage message = new SimpleMailMessage();\n        message.setFrom(username);\n        message.setTo(\"7685413@qq.com\");\n        message.setSubject(\"我是测试主题\");\n        message.setText(\"我是测试内容\");\n\n        mailSender.send(message);\n    }\n\n}\n"
  },
  {
    "path": "lab-50/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-50</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-50-demo</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-50/计划中",
    "content": ""
  },
  {
    "path": "lab-51/lab-51-sentry-logback/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-51-sentry-logback</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- Sentry 对 Logback 的拓展，实现通过打日志的方式，来上传日志到 Sentry 服务 -->\n        <dependency>\n            <groupId>io.sentry</groupId>\n            <artifactId>sentry-logback</artifactId>\n            <version>1.7.30</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-51/lab-51-sentry-logback/src/main/java/cn/iocoder/springboot/lab51/sentrydemo/DemoApplication.java",
    "content": "package cn.iocoder.springboot.lab51.sentrydemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-51/lab-51-sentry-logback/src/main/java/cn/iocoder/springboot/lab51/sentrydemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab51.sentrydemo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/sentry\")\n    public String sentry() {\n        logger.error(\"[main][我就是展示下异常！]\");\n\n        return \"success\";\n    }\n\n    @GetMapping(\"/exception\")\n    public String exception() {\n        throw new RuntimeException(\"直接抛出异常\");\n    }\n\n}\n"
  },
  {
    "path": "lab-51/lab-51-sentry-logback/src/main/java/cn/iocoder/springboot/lab51/sentrydemo/core/package-info.java",
    "content": "package cn.iocoder.springboot.lab51.sentrydemo.core;\n"
  },
  {
    "path": "lab-51/lab-51-sentry-logback/src/main/java/cn/iocoder/springboot/lab51/sentrydemo/core/web/GlobalExceptionHandler.java",
    "content": "package cn.iocoder.springboot.lab51.sentrydemo.core.web;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\nimport javax.servlet.http.HttpServletRequest;\n\n@ControllerAdvice(basePackages = \"cn.iocoder.springboot.lab51.sentrydemo.controller\")\npublic class GlobalExceptionHandler {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    // ... 省略其它类型异常的处理\n\n    /**\n     * 处理其它 Exception 异常\n     */\n    @ResponseBody\n    @ExceptionHandler(value = Exception.class)\n    public String exceptionHandler(HttpServletRequest req, Exception e) {\n        // 记录异常日志\n        logger.error(\"[exceptionHandler]\", e);\n        // 返回 ERROR CommonResult\n        return \"系统异常\";\n    }\n\n}\n"
  },
  {
    "path": "lab-51/lab-51-sentry-logback/src/main/resources/logback-spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<configuration>\n\n    <!-- 参考  -->\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n    <property name=\"LOG_FILE\" value=\"${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}\"/>\n    <include resource=\"org/springframework/boot/logging/logback/console-appender.xml\" />\n    <include resource=\"org/springframework/boot/logging/logback/file-appender.xml\" />\n\n    <!-- 定义 Sentry Appender -->\n    <appender name=\"SentryAppender\" class=\"io.sentry.logback.SentryAppender\">\n        <filter class=\"ch.qos.logback.classic.filter.ThresholdFilter\">\n            <level>WARN</level>\n        </filter>\n        <!-- 使用默认的 SentryAppender encoder 格式 -->\n        <encoder>\n            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>\n        </encoder>\n    </appender>\n\n    <!-- 日志输出级别 -->\n    <root level=\"INFO\">\n        <appender-ref ref=\"CONSOLE\" />\n        <appender-ref ref=\"FILE\" />\n        <appender-ref ref=\"SentryAppender\" />\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "lab-51/lab-51-sentry-logback/src/main/resources/sentry.properties",
    "content": "# Sentry 配置文件。每个配置 key，从 DefaultSentryClientFactory 类中寻找\n# DSN\ndsn=http://b10468b71cd945358ff88ed975287194@47.56.150.64:9000/3\n# HTTP 请求建立连接超时时间\ntimeout=60000\n# HTTP 请求等待结果超时时间\nreadtimeout=60000\n"
  },
  {
    "path": "lab-51/lab-51-sentry-spring/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-51-sentry-spring</artifactId>\n\n    <dependencies>\n        <!-- sentry-spring 的 Spring Boot Starter 库，实现它的自动配置 -->\n        <dependency>\n            <groupId>io.sentry</groupId>\n            <artifactId>sentry-spring-boot-starter</artifactId>\n            <version>1.7.30</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-51/lab-51-sentry-spring/src/main/java/cn/iocoder/springboot/lab51/sentrydemo/DemoApplication.java",
    "content": "package cn.iocoder.springboot.lab51.sentrydemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) throws InterruptedException {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-51/lab-51-sentry-spring/src/main/java/cn/iocoder/springboot/lab51/sentrydemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab51.sentrydemo.controller;\n\nimport io.sentry.SentryClient;\nimport io.sentry.event.EventBuilder;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private SentryClient sentryClient;\n\n    @GetMapping(\"/sentry\")\n    public String sentry() {\n        // 上传消息到 Sentry 中\n        sentryClient.sendMessage(\"示例消息\");\n\n        // 上传异常到 Sentry 中\n        sentryClient.sendException(new RuntimeException(\"测试异常\"));\n\n        // 上传事件到 Sentry 中\n        sentryClient.sendEvent(new EventBuilder().withMessage(\"示例事件\").build());\n\n        return \"success\";\n    }\n\n    @GetMapping(\"/exception\")\n    public String exception() {\n        throw new RuntimeException(\"直接抛出异常\");\n    }\n\n}\n"
  },
  {
    "path": "lab-51/lab-51-sentry-spring/src/main/resources/application.yml",
    "content": "# Sentry 配置项，对应 SentryProperties 配置类\nsentry:\n  dsn: http://b10468b71cd945358ff88ed975287194@47.56.150.64:9000/3 # DSN\n  timeout: 60000 # HTTP 请求建立连接超时时间\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "lab-51/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-51</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-51-sentry-logback</module>\n        <module>lab-51-sentry-spring</module>\n<!--        <module>lab-51-sentry-logback-spring</module>-->\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-51/《芋道 Spring Boot 异常管理平台 Sentry 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Sentry/?github>\n"
  },
  {
    "path": "lab-52/lab-52-multiple-datasource/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-52-multiple-datasource</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.2</version>\n        </dependency>\n\n        <!-- 实现对 dynamic-datasource 的自动化配置 -->\n        <dependency>\n            <groupId>com.baomidou</groupId>\n            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>\n            <version>3.0.0</version>\n        </dependency>\n\n        <!-- 实现对 Seata 的自动化配置 -->\n        <dependency>\n            <groupId>io.seata</groupId>\n            <artifactId>seata-spring-boot-starter</artifactId>\n            <version>1.1.0</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/MultipleDatasourceApplication.java",
    "content": "package cn.iocoder.springboot.lab52.seatademo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class MultipleDatasourceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(MultipleDatasourceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/controller/OrderController.java",
    "content": "package cn.iocoder.springboot.lab52.seatademo.controller;\n\nimport cn.iocoder.springboot.lab52.seatademo.service.OrderService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/order\")\npublic class OrderController {\n\n    private Logger logger = LoggerFactory.getLogger(OrderController.class);\n\n    @Autowired\n    private OrderService orderService;\n\n    @PostMapping(\"/create\")\n    public Integer createOrder(@RequestParam(\"userId\") Long userId,\n                               @RequestParam(\"productId\") Long productId,\n                               @RequestParam(\"price\") Integer price) throws Exception {\n        logger.info(\"[createOrder] 收到下单请求,用户:{}, 商品:{}, 价格:{}\", userId, productId, price);\n        return orderService.createOrder(userId, productId, price);\n    }\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/dao/AccountDao.java",
    "content": "package cn.iocoder.springboot.lab52.seatademo.dao;\n\n\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\nimport org.apache.ibatis.annotations.Update;\nimport org.springframework.stereotype.Repository;\n\n@Mapper\n@Repository\npublic interface AccountDao {\n\n    /**\n     * 获取账户余额\n     *\n     * @param userId 用户 ID\n     * @return 账户余额\n     */\n    @Select(\"SELECT balance FROM account WHERE id = #{userId}\")\n    Integer getBalance(@Param(\"userId\") Long userId);\n\n    /**\n     * 扣减余额\n     *\n     * @param price 需要扣减的数目\n     * @return 影响记录行数\n     */\n    @Update(\"UPDATE account SET balance = balance - #{price} WHERE id = 1 AND balance >= ${price}\")\n    int reduceBalance(@Param(\"price\") Integer price);\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/dao/OrderDao.java",
    "content": "package cn.iocoder.springboot.lab52.seatademo.dao;\n\nimport cn.iocoder.springboot.lab52.seatademo.entity.OrderDO;\nimport org.apache.ibatis.annotations.*;\nimport org.springframework.stereotype.Repository;\n\n@Mapper\n@Repository\npublic interface OrderDao {\n\n    /**\n     * 插入订单记录\n     *\n     * @param order 订单\n     * @return 影响记录数量\n     */\n    @Insert(\"INSERT INTO orders (user_id, product_id, pay_amount) VALUES (#{userId}, #{productId}, #{payAmount})\")\n    @Options(useGeneratedKeys = true, keyColumn = \"id\", keyProperty = \"id\")\n    int saveOrder(OrderDO order);\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/dao/ProductDao.java",
    "content": "package cn.iocoder.springboot.lab52.seatademo.dao;\n\n\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\nimport org.apache.ibatis.annotations.Update;\nimport org.springframework.stereotype.Repository;\n\n@Mapper\n@Repository\npublic interface ProductDao {\n\n    /**\n     * 获取库存\n     *\n     * @param productId 商品编号\n     * @return 库存\n     */\n    @Select(\"SELECT stock FROM product WHERE id = #{productId}\")\n    Integer getStock(@Param(\"productId\") Long productId);\n\n    /**\n     * 扣减库存\n     *\n     * @param productId 商品编号\n     * @param amount    扣减数量\n     * @return 影响记录行数\n     */\n    @Update(\"UPDATE product SET stock = stock - #{amount} WHERE id = #{productId} AND stock >= #{amount}\")\n    int reduceStock(@Param(\"productId\") Long productId, @Param(\"amount\") Integer amount);\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/entity/OrderDO.java",
    "content": "package cn.iocoder.springboot.lab52.seatademo.entity;\n\n/**\n * 订单实体\n */\npublic class OrderDO {\n\n    /** 订单编号 **/\n    private Integer id;\n\n    /** 用户编号 **/\n    private Long userId;\n\n    /** 产品编号 **/\n    private Long productId;\n\n    /** 支付金额 **/\n    private Integer payAmount;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public OrderDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Long getUserId() {\n        return userId;\n    }\n\n    public OrderDO setUserId(Long userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    public Long getProductId() {\n        return productId;\n    }\n\n    public OrderDO setProductId(Long productId) {\n        this.productId = productId;\n        return this;\n    }\n\n    public Integer getPayAmount() {\n        return payAmount;\n    }\n\n    public OrderDO setPayAmount(Integer payAmount) {\n        this.payAmount = payAmount;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/service/AccountService.java",
    "content": "package cn.iocoder.springboot.lab52.seatademo.service;\n\n/**\n * 账户 Service\n */\npublic interface AccountService {\n\n    /**\n     * 扣除余额\n     *\n     * @param userId 用户编号\n     * @param price  扣减金额\n     * @throws Exception 失败时抛出异常\n     */\n    void reduceBalance(Long userId, Integer price) throws Exception;\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/service/OrderService.java",
    "content": "package cn.iocoder.springboot.lab52.seatademo.service;\n\n/**\n * 订单 Service\n */\npublic interface OrderService {\n\n    /**\n     * 创建订单\n     *\n     * @param userId 用户编号\n     * @param productId 产品编号\n     * @param price 价格\n     * @return 订单编号\n     * @throws Exception 创建订单失败，抛出异常\n     */\n    Integer createOrder(Long userId, Long productId, Integer price) throws Exception;\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/service/ProductService.java",
    "content": "package cn.iocoder.springboot.lab52.seatademo.service;\n\n/**\n * 商品 Service\n */\npublic interface ProductService {\n\n    /**\n     * 扣减库存\n     *\n     * @param productId 商品 ID\n     * @param amount    扣减数量\n     * @throws Exception 扣减失败时抛出异常\n     */\n    void reduceStock(Long productId, Integer amount) throws Exception;\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/service/impl/AccountServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab52.seatademo.service.impl;\n\nimport cn.iocoder.springboot.lab52.seatademo.dao.AccountDao;\nimport cn.iocoder.springboot.lab52.seatademo.service.AccountService;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport io.seata.core.context.RootContext;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Propagation;\nimport org.springframework.transaction.annotation.Transactional;\n\n@Service\npublic class AccountServiceImpl implements AccountService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private AccountDao accountDao;\n\n    @Override\n    @DS(value = \"account-ds\")\n    @Transactional(propagation = Propagation.REQUIRES_NEW) // 开启新事物\n    public void reduceBalance(Long userId, Integer price) throws Exception {\n        logger.info(\"[reduceBalance] 当前 XID: {}\", RootContext.getXID());\n\n        // 检查余额\n        checkBalance(userId, price);\n\n        logger.info(\"[reduceBalance] 开始扣减用户 {} 余额\", userId);\n        // 扣除余额\n        int updateCount = accountDao.reduceBalance(price);\n        // 扣除成功\n        if (updateCount == 0) {\n            logger.warn(\"[reduceBalance] 扣除用户 {} 余额失败\", userId);\n            throw new Exception(\"余额不足\");\n        }\n        logger.info(\"[reduceBalance] 扣除用户 {} 余额成功\", userId);\n    }\n\n    private void checkBalance(Long userId, Integer price) throws Exception {\n        logger.info(\"[checkBalance] 检查用户 {} 余额\", userId);\n        Integer balance = accountDao.getBalance(userId);\n        if (balance < price) {\n            logger.warn(\"[checkBalance] 用户 {} 余额不足，当前余额:{}\", userId, balance);\n            throw new Exception(\"余额不足\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/service/impl/OrderServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab52.seatademo.service.impl;\n\nimport cn.iocoder.springboot.lab52.seatademo.dao.OrderDao;\nimport cn.iocoder.springboot.lab52.seatademo.entity.OrderDO;\nimport cn.iocoder.springboot.lab52.seatademo.service.OrderService;\nimport cn.iocoder.springboot.lab52.seatademo.service.AccountService;\nimport cn.iocoder.springboot.lab52.seatademo.service.ProductService;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport io.seata.core.context.RootContext;\nimport io.seata.spring.annotation.GlobalTransactional;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class OrderServiceImpl implements OrderService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private OrderDao orderDao;\n\n    @Autowired\n    private AccountService accountService;\n\n    @Autowired\n    private ProductService productService;\n\n    @Override\n    @DS(value = \"order-ds\")\n    @GlobalTransactional\n    public Integer createOrder(Long userId, Long productId, Integer price) throws Exception {\n        Integer amount = 1; // 购买数量，暂时设置为 1。\n\n        logger.info(\"[createOrder] 当前 XID: {}\", RootContext.getXID());\n\n        // 扣减库存\n        productService.reduceStock(productId, amount);\n\n        // 扣减余额\n        accountService.reduceBalance(userId, price);\n\n        // 保存订单\n        OrderDO order = new OrderDO().setUserId(userId).setProductId(productId).setPayAmount(amount * price);\n        orderDao.saveOrder(order);\n        logger.info(\"[createOrder] 保存订单: {}\", order.getId());\n\n        // 返回订单编号\n        return order.getId();\n    }\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-multiple-datasource/src/main/java/cn/iocoder/springboot/lab52/seatademo/service/impl/ProductServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab52.seatademo.service.impl;\n\nimport cn.iocoder.springboot.lab52.seatademo.dao.ProductDao;\nimport cn.iocoder.springboot.lab52.seatademo.service.ProductService;\nimport com.baomidou.dynamic.datasource.annotation.DS;\nimport io.seata.core.context.RootContext;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Propagation;\nimport org.springframework.transaction.annotation.Transactional;\n\n@Service\npublic class ProductServiceImpl implements ProductService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private ProductDao productDao;\n\n    @Override\n    @DS(value = \"product-ds\")\n    @Transactional(propagation = Propagation.REQUIRES_NEW) // 开启新事物\n    public void reduceStock(Long productId, Integer amount) throws Exception {\n        logger.info(\"[reduceStock] 当前 XID: {}\", RootContext.getXID());\n\n        // 检查库存\n        checkStock(productId, amount);\n\n        logger.info(\"[reduceStock] 开始扣减 {} 库存\", productId);\n        // 扣减库存\n        int updateCount = productDao.reduceStock(productId, amount);\n        // 扣除成功\n        if (updateCount == 0) {\n            logger.warn(\"[reduceStock] 扣除 {} 库存失败\", productId);\n            throw new Exception(\"库存不足\");\n        }\n        // 扣除失败\n        logger.info(\"[reduceStock] 扣除 {} 库存成功\", productId);\n    }\n\n    private void checkStock(Long productId, Integer requiredAmount) throws Exception {\n        logger.info(\"[checkStock] 检查 {} 库存\", productId);\n        Integer stock = productDao.getStock(productId);\n        if (stock < requiredAmount) {\n            logger.warn(\"[checkStock] {} 库存不足，当前库存: {}\", productId, stock);\n            throw new Exception(\"库存不足\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-multiple-datasource/src/main/resources/application.yaml",
    "content": "server:\n  port: 8081 # 端口\n\nspring:\n  application:\n    name: multi-datasource-service  # 应用名\n\n  datasource:\n    # dynamic-datasource-spring-boot-starter 动态数据源的配配项，对应 DynamicDataSourceProperties 类\n    dynamic:\n      primary: order-ds # 设置默认的数据源或者数据源组，默认值即为 master\n      datasource:\n        # 订单 order 数据源配置\n        order-ds:\n          url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n          driver-class-name: com.mysql.jdbc.Driver\n          username: root\n          password:\n        # 账户 pay 数据源配置\n        account-ds:\n          url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n          driver-class-name: com.mysql.jdbc.Driver\n          username: root\n          password:\n        # 商品 product 数据源配置\n        product-ds:\n          url: jdbc:mysql://127.0.0.1:3306/seata_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n          driver-class-name: com.mysql.jdbc.Driver\n          username: root\n          password:\n      seata: true # 是否启动对 Seata 的集成\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      multi-datasource-service-group: default\n    # 分组和 Seata 服务的映射\n    grouplist:\n      default: 127.0.0.1:8091\n"
  },
  {
    "path": "lab-52/lab-52-multiple-datasource/src/main/resources/data.sql",
    "content": "# Order\nDROP DATABASE IF EXISTS seata_order;\nCREATE DATABASE seata_order;\n\nCREATE TABLE seata_order.orders\n(\n    id               INT(11) NOT NULL AUTO_INCREMENT,\n    user_id          INT(11)        DEFAULT NULL,\n    product_id       INT(11)        DEFAULT NULL,\n    pay_amount       DECIMAL(10, 0) DEFAULT NULL,\n    add_time         DATETIME       DEFAULT CURRENT_TIMESTAMP,\n    last_update_time DATETIME       DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\n\nCREATE TABLE seata_order.undo_log\n(\n    id            BIGINT(20)   NOT NULL AUTO_INCREMENT,\n    branch_id     BIGINT(20)   NOT NULL,\n    xid           VARCHAR(100) NOT NULL,\n    context       VARCHAR(128) NOT NULL,\n    rollback_info LONGBLOB     NOT NULL,\n    log_status    INT(11)      NOT NULL,\n    log_created   DATETIME     NOT NULL,\n    log_modified  DATETIME     NOT NULL,\n    PRIMARY KEY (id),\n    UNIQUE KEY ux_undo_log (xid, branch_id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\n\n# Product\nDROP DATABASE IF EXISTS seata_product;\nCREATE DATABASE seata_product;\n\nCREATE TABLE seata_product.product\n(\n    id               INT(11) NOT NULL AUTO_INCREMENT,\n    stock            INT(11)  DEFAULT NULL,\n    last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\nINSERT INTO seata_product.product (id, stock) VALUES (1, 10); # 插入一条产品的库存\n\nCREATE TABLE seata_product.undo_log\n(\n    id            BIGINT(20)   NOT NULL AUTO_INCREMENT,\n    branch_id     BIGINT(20)   NOT NULL,\n    xid           VARCHAR(100) NOT NULL,\n    context       VARCHAR(128) NOT NULL,\n    rollback_info LONGBLOB     NOT NULL,\n    log_status    INT(11)      NOT NULL,\n    log_created   DATETIME     NOT NULL,\n    log_modified  DATETIME     NOT NULL,\n    PRIMARY KEY (id),\n    UNIQUE KEY ux_undo_log (xid, branch_id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\n\n# Account\nDROP DATABASE IF EXISTS seata_account;\nCREATE DATABASE seata_account;\n\nCREATE TABLE seata_account.account\n(\n    id               INT(11) NOT NULL AUTO_INCREMENT,\n    balance          DOUBLE   DEFAULT NULL,\n    last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1  DEFAULT CHARSET = utf8;\n\nCREATE TABLE seata_account.undo_log\n(\n    id            BIGINT(20)   NOT NULL AUTO_INCREMENT,\n    branch_id     BIGINT(20)   NOT NULL,\n    xid           VARCHAR(100) NOT NULL,\n    context       VARCHAR(128) NOT NULL,\n    rollback_info LONGBLOB     NOT NULL,\n    log_status    INT(11)      NOT NULL,\n    log_created   DATETIME     NOT NULL,\n    log_modified  DATETIME     NOT NULL,\n    PRIMARY KEY (id),\n    UNIQUE KEY ux_undo_log (xid, branch_id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\nINSERT INTO seata_account.account (id, balance) VALUES (1, 10);\n\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-52-seata-at-httpclient-demo-account-service</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.2</version>\n        </dependency>\n\n        <!-- 实现对 Seata 的自动化配置 -->\n        <dependency>\n            <groupId>io.seata</groupId>\n            <artifactId>seata-spring-boot-starter</artifactId>\n            <version>1.1.0</version>\n        </dependency>\n        <!-- 实现 Seata 对 HttpClient 的集成支持  -->\n        <dependency>\n            <groupId>io.seata</groupId>\n            <artifactId>seata-http</artifactId>\n            <version>1.1.0</version>\n        </dependency>\n\n        <!-- Apache HttpClient 依赖 -->\n        <dependency>\n            <groupId>org.apache.httpcomponents</groupId>\n            <artifactId>httpclient</artifactId>\n            <version>4.5.8</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/AccountServiceApplication.java",
    "content": "package cn.iocoder.springboot.lab52.accountservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class AccountServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AccountServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/controller/AccountController.java",
    "content": "package cn.iocoder.springboot.lab52.accountservice.controller;\n\nimport cn.iocoder.springboot.lab52.accountservice.dto.AccountReduceBalanceDTO;\nimport cn.iocoder.springboot.lab52.accountservice.service.AccountService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/account\")\npublic class AccountController {\n\n    private Logger logger = LoggerFactory.getLogger(AccountController.class);\n\n    @Autowired\n    private AccountService accountService;\n\n    @PostMapping(\"/reduce-balance\")\n    public Boolean reduceBalance(@RequestBody AccountReduceBalanceDTO accountReduceBalanceDTO) {\n        logger.info(\"[reduceBalance] 收到减少余额请求, 用户:{}, 金额:{}\", accountReduceBalanceDTO.getUserId(),\n                accountReduceBalanceDTO.getPrice());\n        try {\n            accountService.reduceBalance(accountReduceBalanceDTO.getUserId(), accountReduceBalanceDTO.getPrice());\n            // 正常扣除余额，返回 true\n            return true;\n        } catch (Exception e) {\n            // 失败扣除余额，返回 false\n            return false;\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/dao/AccountDao.java",
    "content": "package cn.iocoder.springboot.lab52.accountservice.dao;\n\n\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\nimport org.apache.ibatis.annotations.Update;\nimport org.springframework.stereotype.Repository;\n\n@Mapper\n@Repository\npublic interface AccountDao {\n\n    /**\n     * 获取账户余额\n     *\n     * @param userId 用户 ID\n     * @return 账户余额\n     */\n    @Select(\"SELECT balance FROM account WHERE id = #{userId}\")\n    Integer getBalance(@Param(\"userId\") Long userId);\n\n    /**\n     * 扣减余额\n     *\n     * @param price 需要扣减的数目\n     * @return 影响记录行数\n     */\n    @Update(\"UPDATE account SET balance = balance - #{price} WHERE id = 1 AND balance >= ${price}\")\n    int reduceBalance(@Param(\"price\") Integer price);\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/dto/AccountReduceBalanceDTO.java",
    "content": "package cn.iocoder.springboot.lab52.accountservice.dto;\n\n/**\n * 账户减少余额 DTO\n */\npublic class AccountReduceBalanceDTO {\n\n    /**\n     * 用户编号\n     */\n    private Long userId;\n\n    /**\n     * 扣减金额\n     */\n    private Integer price;\n\n    public Long getUserId() {\n        return userId;\n    }\n\n    public AccountReduceBalanceDTO setUserId(Long userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    public Integer getPrice() {\n        return price;\n    }\n\n    public AccountReduceBalanceDTO setPrice(Integer price) {\n        this.price = price;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/service/AccountService.java",
    "content": "package cn.iocoder.springboot.lab52.accountservice.service;\n\n/**\n * 账户 Service\n */\npublic interface AccountService {\n\n    /**\n     * 扣除余额\n     *\n     * @param userId 用户编号\n     * @param price  扣减金额\n     * @throws Exception 失败时抛出异常\n     */\n    void reduceBalance(Long userId, Integer price) throws Exception;\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/java/cn/iocoder/springboot/lab52/accountservice/service/AccountServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab52.accountservice.service;\n\nimport cn.iocoder.springboot.lab52.accountservice.dao.AccountDao;\nimport io.seata.core.context.RootContext;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\n@Service\npublic class AccountServiceImpl implements AccountService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private AccountDao accountDao;\n\n    @Override\n    @Transactional // 开启新事物\n    public void reduceBalance(Long userId, Integer price) throws Exception {\n        logger.info(\"[reduceBalance] 当前 XID: {}\", RootContext.getXID());\n\n        // 检查余额\n        checkBalance(userId, price);\n\n        logger.info(\"[reduceBalance] 开始扣减用户 {} 余额\", userId);\n        // 扣除余额\n        int updateCount = accountDao.reduceBalance(price);\n        // 扣除成功\n        if (updateCount == 0) {\n            logger.warn(\"[reduceBalance] 扣除用户 {} 余额失败\", userId);\n            throw new Exception(\"余额不足\");\n        }\n        logger.info(\"[reduceBalance] 扣除用户 {} 余额成功\", userId);\n    }\n\n    private void checkBalance(Long userId, Integer price) throws Exception {\n        logger.info(\"[checkBalance] 检查用户 {} 余额\", userId);\n        Integer balance = accountDao.getBalance(userId);\n        if (balance < price) {\n            logger.warn(\"[checkBalance] 用户 {} 余额不足，当前余额:{}\", userId, balance);\n            throw new Exception(\"余额不足\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-account-service/src/main/resources/application.yaml",
    "content": "server:\n  port: 8083\n\nspring:\n  application:\n    name: account-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      account-service-group: default\n    # 分组和 Seata 服务的映射\n    grouplist:\n      default: 127.0.0.1:8091\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-52-seata-at-httpclient-demo-order-service</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.2</version>\n        </dependency>\n\n        <!-- 实现对 Seata 的自动化配置 -->\n        <dependency>\n            <groupId>io.seata</groupId>\n            <artifactId>seata-spring-boot-starter</artifactId>\n            <version>1.1.0</version>\n        </dependency>\n        <!-- 实现 Seata 对 HttpClient 的集成支持  -->\n        <dependency>\n            <groupId>io.seata</groupId>\n            <artifactId>seata-http</artifactId>\n            <version>1.1.0</version>\n        </dependency>\n\n        <!-- Apache HttpClient 依赖 -->\n        <dependency>\n            <groupId>org.apache.httpcomponents</groupId>\n            <artifactId>httpclient</artifactId>\n            <version>4.5.8</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/OrderServiceApplication.java",
    "content": "package cn.iocoder.springboot.lab52.orderservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class OrderServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(OrderServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/controller/OrderController.java",
    "content": "package cn.iocoder.springboot.lab52.orderservice.controller;\n\nimport cn.iocoder.springboot.lab52.orderservice.service.OrderService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/order\")\npublic class OrderController {\n\n    private Logger logger = LoggerFactory.getLogger(OrderController.class);\n\n    @Autowired\n    private OrderService orderService;\n\n    @PostMapping(\"/create\")\n    public Integer createOrder(@RequestParam(\"userId\") Long userId,\n                               @RequestParam(\"productId\") Long productId,\n                               @RequestParam(\"price\") Integer price) throws Exception {\n        logger.info(\"[createOrder] 收到下单请求,用户:{}, 商品:{}, 价格:{}\", userId, productId, price);\n        return orderService.createOrder(userId, productId, price);\n    }\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/dao/OrderDao.java",
    "content": "package cn.iocoder.springboot.lab52.orderservice.dao;\n\nimport cn.iocoder.springboot.lab53.orderservice.entity.OrderDO;\nimport org.apache.ibatis.annotations.Insert;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Options;\nimport org.springframework.stereotype.Repository;\n\n@Mapper\n@Repository\npublic interface OrderDao {\n\n    /**\n     * 插入订单记录\n     *\n     * @param order 订单\n     * @return 影响记录数量\n     */\n    @Insert(\"INSERT INTO orders (user_id, product_id, pay_amount) VALUES (#{userId}, #{productId}, #{payAmount})\")\n    @Options(useGeneratedKeys = true, keyColumn = \"id\", keyProperty = \"id\")\n    int saveOrder(OrderDO order);\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/entity/OrderDO.java",
    "content": "package cn.iocoder.springboot.lab53.orderservice.entity;\n\n/**\n * 订单实体\n */\npublic class OrderDO {\n\n    /** 订单编号 **/\n    private Integer id;\n\n    /** 用户编号 **/\n    private Long userId;\n\n    /** 产品编号 **/\n    private Long productId;\n\n    /** 支付金额 **/\n    private Integer payAmount;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public OrderDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Long getUserId() {\n        return userId;\n    }\n\n    public OrderDO setUserId(Long userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    public Long getProductId() {\n        return productId;\n    }\n\n    public OrderDO setProductId(Long productId) {\n        this.productId = productId;\n        return this;\n    }\n\n    public Integer getPayAmount() {\n        return payAmount;\n    }\n\n    public OrderDO setPayAmount(Integer payAmount) {\n        this.payAmount = payAmount;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/service/OrderService.java",
    "content": "package cn.iocoder.springboot.lab52.orderservice.service;\n\n/**\n * 订单 Service\n */\npublic interface OrderService {\n\n    /**\n     * 创建订单\n     *\n     * @param userId 用户编号\n     * @param productId 产品编号\n     * @param price 价格\n     * @return 订单编号\n     * @throws Exception 创建订单失败，抛出异常\n     */\n    Integer createOrder(Long userId, Long productId, Integer price) throws Exception;\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/java/cn/iocoder/springboot/lab52/orderservice/service/OrderServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab52.orderservice.service;\n\nimport cn.iocoder.springboot.lab52.orderservice.dao.OrderDao;\nimport cn.iocoder.springboot.lab53.orderservice.entity.OrderDO;\nimport com.alibaba.fastjson.JSONObject;\nimport io.seata.core.context.RootContext;\nimport io.seata.integration.http.DefaultHttpExecutor;\nimport io.seata.spring.annotation.GlobalTransactional;\nimport org.apache.http.HttpResponse;\nimport org.apache.http.util.EntityUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\nimport java.io.IOException;\n\n@Service\npublic class OrderServiceImpl implements OrderService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private OrderDao orderDao;\n\n    @Override\n    @GlobalTransactional\n    public Integer createOrder(Long userId, Long productId, Integer price) throws Exception {\n        Integer amount = 1; // 购买数量，暂时设置为 1。\n\n        logger.info(\"[createOrder] 当前 XID: {}\", RootContext.getXID());\n\n        // 扣减库存\n        this.reduceStock(productId, amount);\n\n        // 扣减余额\n        this.reduceBalance(userId, price);\n\n        // 保存订单\n        OrderDO order = new OrderDO().setUserId(userId).setProductId(productId).setPayAmount(amount * price);\n        orderDao.saveOrder(order);\n        logger.info(\"[createOrder] 保存订单: {}\", order.getId());\n\n        // 返回订单编号\n        return order.getId();\n    }\n\n    private void reduceStock(Long productId, Integer amount) throws IOException {\n        // 参数拼接\n        JSONObject params = new JSONObject().fluentPut(\"productId\", String.valueOf(productId))\n                .fluentPut(\"amount\", String.valueOf(amount));\n        // 执行调用\n        HttpResponse response = DefaultHttpExecutor.getInstance().executePost(\"http://127.0.0.1:8082\", \"/product/reduce-stock\",\n                params, HttpResponse.class);\n        // 解析结果\n        Boolean success = Boolean.valueOf(EntityUtils.toString(response.getEntity()));\n        if (!success) {\n            throw new RuntimeException(\"扣除库存失败\");\n        }\n    }\n\n    private void reduceBalance(Long userId, Integer price) throws IOException {\n        // 参数拼接\n        JSONObject params = new JSONObject().fluentPut(\"userId\", String.valueOf(userId))\n                .fluentPut(\"price\", String.valueOf(price));\n        // 执行调用\n        HttpResponse response = DefaultHttpExecutor.getInstance().executePost(\"http://127.0.0.1:8083\", \"/account/reduce-balance\",\n                params, HttpResponse.class);\n        // 解析结果\n        Boolean success = Boolean.valueOf(EntityUtils.toString(response.getEntity()));\n        if (!success) {\n            throw new RuntimeException(\"扣除余额失败\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-order-service/src/main/resources/application.yaml",
    "content": "server:\n  port: 8081 # 端口\n\nspring:\n  application:\n    name: order-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      order-service-group: default\n    # 分组和 Seata 服务的映射\n    grouplist:\n      default: 127.0.0.1:8091\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-52-seata-at-httpclient-demo-product-service</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.2</version>\n        </dependency>\n\n        <!-- 实现对 Seata 的自动化配置 -->\n        <dependency>\n            <groupId>io.seata</groupId>\n            <artifactId>seata-spring-boot-starter</artifactId>\n            <version>1.1.0</version>\n        </dependency>\n        <!-- 实现 Seata 对 HttpClient 的集成支持  -->\n        <dependency>\n            <groupId>io.seata</groupId>\n            <artifactId>seata-http</artifactId>\n            <version>1.1.0</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/ProductServiceApplication.java",
    "content": "package cn.iocoder.springboot.lab52.productservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProductServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProductServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/controller/ProductController.java",
    "content": "package cn.iocoder.springboot.lab52.productservice.controller;\n\nimport cn.iocoder.springboot.lab52.productservice.dto.ProductReduceStockDTO;\nimport cn.iocoder.springboot.lab52.productservice.service.ProductService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\n@RequestMapping(\"/product\")\npublic class ProductController {\n\n    private Logger logger = LoggerFactory.getLogger(ProductController.class);\n\n    @Autowired\n    private ProductService productService;\n\n    @PostMapping(\"/reduce-stock\")\n    public Boolean reduceStock(@RequestBody ProductReduceStockDTO productReduceStockDTO) {\n        logger.info(\"[reduceStock] 收到减少库存请求, 商品:{}, 价格:{}\", productReduceStockDTO.getProductId(),\n                productReduceStockDTO.getAmount());\n        try {\n            productService.reduceStock(productReduceStockDTO.getProductId(), productReduceStockDTO.getAmount());\n            // 正常扣除库存，返回 true\n            return true;\n        } catch (Exception e) {\n            // 失败扣除库存，返回 false\n            return false;\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/dao/ProductDao.java",
    "content": "package cn.iocoder.springboot.lab52.productservice.dao;\n\n\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\nimport org.apache.ibatis.annotations.Update;\nimport org.springframework.stereotype.Repository;\n\n@Mapper\n@Repository\npublic interface ProductDao {\n\n    /**\n     * 获取库存\n     *\n     * @param productId 商品编号\n     * @return 库存\n     */\n    @Select(\"SELECT stock FROM product WHERE id = #{productId}\")\n    Integer getStock(@Param(\"productId\") Long productId);\n\n    /**\n     * 扣减库存\n     *\n     * @param productId 商品编号\n     * @param amount    扣减数量\n     * @return 影响记录行数\n     */\n    @Update(\"UPDATE product SET stock = stock - #{amount} WHERE id = #{productId} AND stock >= #{amount}\")\n    int reduceStock(@Param(\"productId\") Long productId, @Param(\"amount\") Integer amount);\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/dto/ProductReduceStockDTO.java",
    "content": "package cn.iocoder.springboot.lab52.productservice.dto;\n\n/**\n * 商品减少库存 DTO\n */\npublic class ProductReduceStockDTO {\n\n    /**\n     * 商品编号\n     */\n    private Long productId;\n    /**\n     * 数量\n     */\n    private Integer amount;\n\n    public Long getProductId() {\n        return productId;\n    }\n\n    public ProductReduceStockDTO setProductId(Long productId) {\n        this.productId = productId;\n        return this;\n    }\n\n    public Integer getAmount() {\n        return amount;\n    }\n\n    public ProductReduceStockDTO setAmount(Integer amount) {\n        this.amount = amount;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/service/ProductService.java",
    "content": "package cn.iocoder.springboot.lab52.productservice.service;\n\n/**\n * 商品 Service\n */\npublic interface ProductService {\n\n    /**\n     * 扣减库存\n     *\n     * @param productId 商品 ID\n     * @param amount    扣减数量\n     * @throws Exception 扣减失败时抛出异常\n     */\n    void reduceStock(Long productId, Integer amount) throws Exception;\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/java/cn/iocoder/springboot/lab52/productservice/service/ProductServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab52.productservice.service;\n\nimport cn.iocoder.springboot.lab52.productservice.dao.ProductDao;\nimport io.seata.core.context.RootContext;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\n@Service\npublic class ProductServiceImpl implements ProductService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private ProductDao productDao;\n\n    @Override\n    @Transactional // 开启事物\n    public void reduceStock(Long productId, Integer amount) throws Exception {\n        logger.info(\"[reduceStock] 当前 XID: {}\", RootContext.getXID());\n\n        // 检查库存\n        checkStock(productId, amount);\n\n        logger.info(\"[reduceStock] 开始扣减 {} 库存\", productId);\n        // 扣减库存\n        int updateCount = productDao.reduceStock(productId, amount);\n        // 扣除成功\n        if (updateCount == 0) {\n            logger.warn(\"[reduceStock] 扣除 {} 库存失败\", productId);\n            throw new Exception(\"库存不足\");\n        }\n        // 扣除失败\n        logger.info(\"[reduceStock] 扣除 {} 库存成功\", productId);\n    }\n\n    private void checkStock(Long productId, Integer requiredAmount) throws Exception {\n        logger.info(\"[checkStock] 检查 {} 库存\", productId);\n        Integer stock = productDao.getStock(productId);\n        if (stock < requiredAmount) {\n            logger.warn(\"[checkStock] {} 库存不足，当前库存: {}\", productId, stock);\n            throw new Exception(\"库存不足\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/lab-52-seata-at-httpclient-demo-product-service/src/main/resources/application.yaml",
    "content": "server:\n  port: 8082 # 端口\n\nspring:\n  application:\n    name: product-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      product-service-group: default\n    # 分组和 Seata 服务的映射\n    grouplist:\n      default: 127.0.0.1:8091\n"
  },
  {
    "path": "lab-52/lab-52-seata-at-httpclient-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-52</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-52-seata-at-httpclient-demo</artifactId>\n\n    <modules>\n        <module>lab-52-seata-at-httpclient-demo-order-service</module>\n        <module>lab-52-seata-at-httpclient-demo-product-service</module>\n        <module>lab-52-seata-at-httpclient-demo-account-service</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-52/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-52</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>lab-52-multiple-datasource</module>\n        <module>lab-52-seata-at-httpclient-demo</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-52/《芋道 Spring Boot 分布式事务 Seata 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Seata/?github>\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/data.sql",
    "content": "# Order\nDROP DATABASE IF EXISTS seata_order;\nCREATE DATABASE seata_order;\n\nCREATE TABLE seata_order.orders\n(\n    id               INT(11) NOT NULL AUTO_INCREMENT,\n    user_id          INT(11)        DEFAULT NULL,\n    product_id       INT(11)        DEFAULT NULL,\n    pay_amount       DECIMAL(10, 0) DEFAULT NULL,\n    add_time         DATETIME       DEFAULT CURRENT_TIMESTAMP,\n    last_update_time DATETIME       DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\n\nCREATE TABLE seata_order.undo_log\n(\n    id            BIGINT(20)   NOT NULL AUTO_INCREMENT,\n    branch_id     BIGINT(20)   NOT NULL,\n    xid           VARCHAR(100) NOT NULL,\n    context       VARCHAR(128) NOT NULL,\n    rollback_info LONGBLOB     NOT NULL,\n    log_status    INT(11)      NOT NULL,\n    log_created   DATETIME     NOT NULL,\n    log_modified  DATETIME     NOT NULL,\n    PRIMARY KEY (id),\n    UNIQUE KEY ux_undo_log (xid, branch_id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\n\n# Product\nDROP DATABASE IF EXISTS seata_product;\nCREATE DATABASE seata_product;\n\nCREATE TABLE seata_product.product\n(\n    id               INT(11) NOT NULL AUTO_INCREMENT,\n    stock            INT(11)  DEFAULT NULL,\n    last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\nINSERT INTO seata_product.product (id, stock) VALUES (1, 10); # 插入一条产品的库存\n\nCREATE TABLE seata_product.undo_log\n(\n    id            BIGINT(20)   NOT NULL AUTO_INCREMENT,\n    branch_id     BIGINT(20)   NOT NULL,\n    xid           VARCHAR(100) NOT NULL,\n    context       VARCHAR(128) NOT NULL,\n    rollback_info LONGBLOB     NOT NULL,\n    log_status    INT(11)      NOT NULL,\n    log_created   DATETIME     NOT NULL,\n    log_modified  DATETIME     NOT NULL,\n    PRIMARY KEY (id),\n    UNIQUE KEY ux_undo_log (xid, branch_id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\n\n# Account\nDROP DATABASE IF EXISTS seata_account;\nCREATE DATABASE seata_account;\n\nCREATE TABLE seata_account.account\n(\n    id               INT(11) NOT NULL AUTO_INCREMENT,\n    balance          DOUBLE   DEFAULT NULL,\n    last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1  DEFAULT CHARSET = utf8;\n\nCREATE TABLE seata_account.undo_log\n(\n    id            BIGINT(20)   NOT NULL AUTO_INCREMENT,\n    branch_id     BIGINT(20)   NOT NULL,\n    xid           VARCHAR(100) NOT NULL,\n    context       VARCHAR(128) NOT NULL,\n    rollback_info LONGBLOB     NOT NULL,\n    log_status    INT(11)      NOT NULL,\n    log_created   DATETIME     NOT NULL,\n    log_modified  DATETIME     NOT NULL,\n    PRIMARY KEY (id),\n    UNIQUE KEY ux_undo_log (xid, branch_id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\nINSERT INTO seata_account.account (id, balance) VALUES (1, 10);\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-account-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-53-seata-at-dubbo-demo-account-service</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-53-seata-at-dubbo-demo-account-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- Spring Boot Starter 基础库 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.2</version>\n        </dependency>\n\n        <!-- 实现对 Seata 的自动化配置 -->\n        <dependency>\n            <groupId>io.seata</groupId>\n            <artifactId>seata-spring-boot-starter</artifactId>\n            <version>1.1.0</version>\n        </dependency>\n\n        <!-- 实现对 Dubbo 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n        <!-- 使用 Nacos 作为注册中心 -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>dubbo-registry-nacos</artifactId>\n            <version>2.7.6</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-account-service/src/main/java/cn/iocoder/springboot/lab53/accountservice/AccountServiceApplication.java",
    "content": "package cn.iocoder.springboot.lab53.accountservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class AccountServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AccountServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-account-service/src/main/java/cn/iocoder/springboot/lab53/accountservice/dao/AccountDao.java",
    "content": "package cn.iocoder.springboot.lab53.accountservice.dao;\n\n\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\nimport org.apache.ibatis.annotations.Update;\nimport org.springframework.stereotype.Repository;\n\n@Mapper\n@Repository\npublic interface AccountDao {\n\n    /**\n     * 获取账户余额\n     *\n     * @param userId 用户 ID\n     * @return 账户余额\n     */\n    @Select(\"SELECT balance FROM account WHERE id = #{userId}\")\n    Integer getBalance(@Param(\"userId\") Long userId);\n\n    /**\n     * 扣减余额\n     *\n     * @param price 需要扣减的数目\n     * @return 影响记录行数\n     */\n    @Update(\"UPDATE account SET balance = balance - #{price} WHERE id = 1 AND balance >= ${price}\")\n    int reduceBalance(@Param(\"price\") Integer price);\n\n}\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-account-service/src/main/java/cn/iocoder/springboot/lab53/accountservice/service/AccountServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab53.accountservice.service;\n\nimport cn.iocoder.springboot.lab53.accountservice.api.AccountService;\nimport cn.iocoder.springboot.lab53.accountservice.dao.AccountDao;\nimport io.seata.core.context.RootContext;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.transaction.annotation.Transactional;\n\n@org.apache.dubbo.config.annotation.Service\npublic class AccountServiceImpl implements AccountService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private AccountDao accountDao;\n\n    @Override\n    @Transactional // 开启新事物\n    public void reduceBalance(Long userId, Integer price) throws Exception {\n        logger.info(\"[reduceBalance] 当前 XID: {}\", RootContext.getXID());\n\n        // 检查余额\n        checkBalance(userId, price);\n\n        logger.info(\"[reduceBalance] 开始扣减用户 {} 余额\", userId);\n        // 扣除余额\n        int updateCount = accountDao.reduceBalance(price);\n        // 扣除成功\n        if (updateCount == 0) {\n            logger.warn(\"[reduceBalance] 扣除用户 {} 余额失败\", userId);\n            throw new Exception(\"余额不足\");\n        }\n        logger.info(\"[reduceBalance] 扣除用户 {} 余额成功\", userId);\n    }\n\n    private void checkBalance(Long userId, Integer price) throws Exception {\n        logger.info(\"[checkBalance] 检查用户 {} 余额\", userId);\n        Integer balance = accountDao.getBalance(userId);\n        if (balance < price) {\n            logger.warn(\"[checkBalance] 用户 {} 余额不足，当前余额:{}\", userId, balance);\n            throw new Exception(\"余额不足\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-account-service/src/main/resources/application-file.yaml",
    "content": "spring:\n  application:\n    name: account-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: ${spring.application.name} # 应用名\n  # Dubbo 注册中心配\n  registry:\n    address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springboot.lab53.accountservice.service\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      account-service-group: default\n    # 分组和 Seata 服务的映射\n    grouplist:\n      default: 127.0.0.1:8091\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-account-service/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: account-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: ${spring.application.name} # 应用名\n  # Dubbo 注册中心配\n  registry:\n    address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springboot.lab53.accountservice.service\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # Seata 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      account-service-group: default\n  # Seata 注册中心配置项，对应 RegistryProperties 类\n  registry:\n    type: nacos # 注册中心类型，默认为 file\n    nacos:\n      cluster: default # 使用的 Seata 分组\n      namespace: # Nacos 命名空间\n      serverAddr: localhost # Nacos 服务地址\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-account-service-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-53-seata-at-dubbo-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-53-seata-at-dubbo-demo-account-service-api</artifactId>\n\n</project>\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-account-service-api/src/main/java/cn/iocoder/springboot/lab53/accountservice/api/AccountService.java",
    "content": "package cn.iocoder.springboot.lab53.accountservice.api;\n\n/**\n * 账户 Service\n */\npublic interface AccountService {\n\n    /**\n     * 扣除余额\n     *\n     * @param userId 用户编号\n     * @param price  扣减金额\n     * @throws Exception 失败时抛出异常\n     */\n    void reduceBalance(Long userId, Integer price) throws Exception;\n\n}\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-order-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-53-seata-at-dubbo-demo-order-service</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-53-seata-at-dubbo-demo-order-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-53-seata-at-dubbo-demo-account-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-53-seata-at-dubbo-demo-product-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.2</version>\n        </dependency>\n\n        <!-- 实现对 Seata 的自动化配置 -->\n        <dependency>\n            <groupId>io.seata</groupId>\n            <artifactId>seata-spring-boot-starter</artifactId>\n            <version>1.1.0</version>\n        </dependency>\n\n        <!-- 引入 Dubbo 的依赖 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <!-- 实现对 Dubbo 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <!-- 实现 Dubbo 使用 Nacos 作为注册中心 -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>dubbo-registry-nacos</artifactId>\n            <version>2.7.6</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-order-service/src/main/java/cn/iocoder/springboot/lab53/orderservice/OrderServiceApplication.java",
    "content": "package cn.iocoder.springboot.lab53.orderservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class OrderServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(OrderServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-order-service/src/main/java/cn/iocoder/springboot/lab53/orderservice/controller/OrderController.java",
    "content": "package cn.iocoder.springboot.lab53.orderservice.controller;\n\nimport cn.iocoder.springboot.lab53.orderservice.api.OrderService;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/order\")\npublic class OrderController {\n\n    private Logger logger = LoggerFactory.getLogger(OrderController.class);\n\n    @Reference\n    private OrderService orderService;\n\n    @PostMapping(\"/create\")\n    public Integer createOrder(@RequestParam(\"userId\") Long userId,\n                               @RequestParam(\"productId\") Long productId,\n                               @RequestParam(\"price\") Integer price) throws Exception {\n        logger.info(\"[createOrder] 收到下单请求,用户:{}, 商品:{}, 价格:{}\", userId, productId, price);\n        return orderService.createOrder(userId, productId, price);\n    }\n\n}\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-order-service/src/main/java/cn/iocoder/springboot/lab53/orderservice/dao/OrderDao.java",
    "content": "package cn.iocoder.springboot.lab53.orderservice.dao;\n\nimport cn.iocoder.springboot.lab53.orderservice.entity.OrderDO;\nimport org.apache.ibatis.annotations.Insert;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Options;\nimport org.springframework.stereotype.Repository;\n\n@Mapper\n@Repository\npublic interface OrderDao {\n\n    /**\n     * 插入订单记录\n     *\n     * @param order 订单\n     * @return 影响记录数量\n     */\n    @Insert(\"INSERT INTO orders (user_id, product_id, pay_amount) VALUES (#{userId}, #{productId}, #{payAmount})\")\n    @Options(useGeneratedKeys = true, keyColumn = \"id\", keyProperty = \"id\")\n    int saveOrder(OrderDO order);\n\n}\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-order-service/src/main/java/cn/iocoder/springboot/lab53/orderservice/entity/OrderDO.java",
    "content": "package cn.iocoder.springboot.lab53.orderservice.entity;\n\n/**\n * 订单实体\n */\npublic class OrderDO {\n\n    /** 订单编号 **/\n    private Integer id;\n\n    /** 用户编号 **/\n    private Long userId;\n\n    /** 产品编号 **/\n    private Long productId;\n\n    /** 支付金额 **/\n    private Integer payAmount;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public OrderDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Long getUserId() {\n        return userId;\n    }\n\n    public OrderDO setUserId(Long userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    public Long getProductId() {\n        return productId;\n    }\n\n    public OrderDO setProductId(Long productId) {\n        this.productId = productId;\n        return this;\n    }\n\n    public Integer getPayAmount() {\n        return payAmount;\n    }\n\n    public OrderDO setPayAmount(Integer payAmount) {\n        this.payAmount = payAmount;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-order-service/src/main/java/cn/iocoder/springboot/lab53/orderservice/service/OrderServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab53.orderservice.service;\n\nimport cn.iocoder.springboot.lab53.accountservice.api.AccountService;\nimport cn.iocoder.springboot.lab53.orderservice.api.OrderService;\nimport cn.iocoder.springboot.lab53.orderservice.dao.OrderDao;\nimport cn.iocoder.springboot.lab53.orderservice.entity.OrderDO;\nimport cn.iocoder.springboot.lab53.productservice.api.ProductService;\nimport io.seata.core.context.RootContext;\nimport io.seata.spring.annotation.GlobalTransactional;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n@org.apache.dubbo.config.annotation.Service\npublic class OrderServiceImpl implements OrderService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private OrderDao orderDao;\n\n    @Reference\n    private AccountService accountService;\n    @Reference\n    private ProductService productService;\n\n    @Override\n    @GlobalTransactional\n    public Integer createOrder(Long userId, Long productId, Integer price) throws Exception {\n        Integer amount = 1; // 购买数量，暂时设置为 1。\n\n        logger.info(\"[createOrder] 当前 XID: {}\", RootContext.getXID());\n\n        // 扣减库存\n        productService.reduceStock(productId, amount);\n\n        // 扣减余额\n        accountService.reduceBalance(userId, price);\n\n        // 保存订单\n        OrderDO order = new OrderDO().setUserId(userId).setProductId(productId).setPayAmount(amount * price);\n        orderDao.saveOrder(order);\n        logger.info(\"[createOrder] 保存订单: {}\", order.getId());\n\n        // 返回订单编号\n        return order.getId();\n    }\n\n}\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-order-service/src/main/resources/application-file.yaml",
    "content": "server:\n  port: 8081 # 端口\n\nspring:\n  application:\n    name: order-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: ${spring.application.name} # 应用名\n  # Dubbo 注册中心配\n  registry:\n    address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springboot.lab53.orderservice.service\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      order-service-group: default\n    # 分组和 Seata 服务的映射\n    grouplist:\n      default: 127.0.0.1:8091\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-order-service/src/main/resources/application.yaml",
    "content": "server:\n  port: 8081 # 端口\n\nspring:\n  application:\n    name: order-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: ${spring.application.name} # 应用名\n  # Dubbo 注册中心配\n  registry:\n    address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springboot.lab53.orderservice.service\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # Seata 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      order-service-group: default\n  # Seata 注册中心配置项，对应 RegistryProperties 类\n  registry:\n    type: nacos # 注册中心类型，默认为 file\n    nacos:\n      cluster: default # 使用的 Seata 分组\n      namespace: # Nacos 命名空间\n      serverAddr: localhost # Nacos 服务地址\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-order-service-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-53-seata-at-dubbo-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-53-seata-at-dubbo-demo-order-service-api</artifactId>\n\n</project>\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-order-service-api/src/main/java/cn/iocoder/springboot/lab53/orderservice/api/OrderService.java",
    "content": "package cn.iocoder.springboot.lab53.orderservice.api;\n\n/**\n * 订单 Service\n */\npublic interface OrderService {\n\n    /**\n     * 创建订单\n     *\n     * @param userId 用户编号\n     * @param productId 产品编号\n     * @param price 价格\n     * @return 订单编号\n     * @throws Exception 创建订单失败，抛出异常\n     */\n    Integer createOrder(Long userId, Long productId, Integer price) throws Exception;\n\n}\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-product-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-53-seata-at-dubbo-demo-product-service</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-53-seata-at-dubbo-demo-product-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- Spring Boot Starter 基础库 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.2</version>\n        </dependency>\n\n        <!-- 实现对 Seata 的自动化配置 -->\n        <dependency>\n            <groupId>io.seata</groupId>\n            <artifactId>seata-spring-boot-starter</artifactId>\n            <version>1.1.0</version>\n        </dependency>\n\n        <!-- 引入 Dubbo 的依赖 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <!-- 实现对 Dubbo 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <!-- 实现 Dubbo 使用 Nacos 作为注册中心 -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>dubbo-registry-nacos</artifactId>\n            <version>2.7.6</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-product-service/src/main/java/cn/iocoder/springboot/lab53/productservice/ProductServiceApplication.java",
    "content": "package cn.iocoder.springboot.lab53.productservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProductServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProductServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-product-service/src/main/java/cn/iocoder/springboot/lab53/productservice/dao/ProductDao.java",
    "content": "package cn.iocoder.springboot.lab53.productservice.dao;\n\n\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\nimport org.apache.ibatis.annotations.Update;\nimport org.springframework.stereotype.Repository;\n\n@Mapper\n@Repository\npublic interface ProductDao {\n\n    /**\n     * 获取库存\n     *\n     * @param productId 商品编号\n     * @return 库存\n     */\n    @Select(\"SELECT stock FROM product WHERE id = #{productId}\")\n    Integer getStock(@Param(\"productId\") Long productId);\n\n    /**\n     * 扣减库存\n     *\n     * @param productId 商品编号\n     * @param amount    扣减数量\n     * @return 影响记录行数\n     */\n    @Update(\"UPDATE product SET stock = stock - #{amount} WHERE id = #{productId} AND stock >= #{amount}\")\n    int reduceStock(@Param(\"productId\") Long productId, @Param(\"amount\") Integer amount);\n\n}\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-product-service/src/main/java/cn/iocoder/springboot/lab53/productservice/service/ProductServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab53.productservice.service;\n\nimport cn.iocoder.springboot.lab53.productservice.dao.ProductDao;\nimport cn.iocoder.springboot.lab53.productservice.api.ProductService;\nimport io.seata.core.context.RootContext;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.transaction.annotation.Transactional;\n\n@org.apache.dubbo.config.annotation.Service\npublic class ProductServiceImpl implements ProductService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private ProductDao productDao;\n\n    @Override\n    @Transactional // 开启新事物\n    public void reduceStock(Long productId, Integer amount) throws Exception {\n        logger.info(\"[reduceStock] 当前 XID: {}\", RootContext.getXID());\n\n        // 检查库存\n        checkStock(productId, amount);\n\n        logger.info(\"[reduceStock] 开始扣减 {} 库存\", productId);\n        // 扣减库存\n        int updateCount = productDao.reduceStock(productId, amount);\n        // 扣除成功\n        if (updateCount == 0) {\n            logger.warn(\"[reduceStock] 扣除 {} 库存失败\", productId);\n            throw new Exception(\"库存不足\");\n        }\n        // 扣除失败\n        logger.info(\"[reduceStock] 扣除 {} 库存成功\", productId);\n    }\n\n    private void checkStock(Long productId, Integer requiredAmount) throws Exception {\n        logger.info(\"[checkStock] 检查 {} 库存\", productId);\n        Integer stock = productDao.getStock(productId);\n        if (stock < requiredAmount) {\n            logger.warn(\"[checkStock] {} 库存不足，当前库存: {}\", productId, stock);\n            throw new Exception(\"库存不足\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-product-service/src/main/resources/application-file.yaml",
    "content": "spring:\n  application:\n    name: product-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: ${spring.application.name} # 应用名\n  # Dubbo 注册中心配\n  registry:\n    address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springboot.lab53.productservice.service\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      product-service-group: default\n    # 分组和 Seata 服务的映射\n    grouplist:\n      default: 127.0.0.1:8091\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-product-service/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: product-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: ${spring.application.name} # 应用名\n  # Dubbo 注册中心配\n  registry:\n    address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springboot.lab53.productservice.service\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # Seata 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      product-service-group: default\n  # Seata 注册中心配置项，对应 RegistryProperties 类\n  registry:\n    type: nacos # 注册中心类型，默认为 file\n    nacos:\n      cluster: default # 使用的 Seata 分组\n      namespace: # Nacos 命名空间\n      serverAddr: localhost # Nacos 服务地址\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-product-service-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-53-seata-at-dubbo-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-53-seata-at-dubbo-demo-product-service-api</artifactId>\n\n</project>\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/lab-53-seata-at-dubbo-demo-product-service-api/src/main/java/cn/iocoder/springboot/lab53/productservice/api/ProductService.java",
    "content": "package cn.iocoder.springboot.lab53.productservice.api;\n\n/**\n * 商品 Service\n */\npublic interface ProductService {\n\n    /**\n     * 扣减库存\n     *\n     * @param productId 商品 ID\n     * @param amount    扣减数量\n     * @throws Exception 扣减失败时抛出异常\n     */\n    void reduceStock(Long productId, Integer amount) throws Exception;\n\n}\n"
  },
  {
    "path": "lab-53/lab-53-seata-at-dubbo-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-53</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-53-seata-at-dubbo-demo</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>lab-53-seata-at-dubbo-demo-order-service-api</module>\n        <module>lab-53-seata-at-dubbo-demo-order-service</module>\n        <module>lab-53-seata-at-dubbo-demo-account-service-api</module>\n        <module>lab-53-seata-at-dubbo-demo-account-service</module>\n        <module>lab-53-seata-at-dubbo-demo-product-service-api</module>\n        <module>lab-53-seata-at-dubbo-demo-product-service</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-53/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-53</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>lab-53-seata-at-dubbo-demo</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-53/《Dubbo 分布式事务 Seata 入门》.md",
    "content": "<http://www.iocoder.cn/Dubbo/Seata/?github>\n"
  },
  {
    "path": "lab-54/lab-54-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-54-demo</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-54/lab-54-demo/src/main/java/cn/iocoder/springboot/lab54/eventdemo/DemoApplication.java",
    "content": "package cn.iocoder.springboot.lab54.eventdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.scheduling.annotation.EnableAsync;\n\n@SpringBootApplication\n@EnableAsync // 开启 Spring 异步的功能\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-54/lab-54-demo/src/main/java/cn/iocoder/springboot/lab54/eventdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab54.eventdemo.controller;\n\nimport cn.iocoder.springboot.lab54.eventdemo.service.UserService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private UserService userService;\n\n    @GetMapping(\"/register\")\n    public String register(String username) {\n        userService.register(username);\n        return \"success\";\n    }\n\n}\n"
  },
  {
    "path": "lab-54/lab-54-demo/src/main/java/cn/iocoder/springboot/lab54/eventdemo/event/UserRegisterEvent.java",
    "content": "package cn.iocoder.springboot.lab54.eventdemo.event;\n\nimport org.springframework.context.ApplicationEvent;\n\n/**\n * 用户注册事件\n */\npublic class UserRegisterEvent extends ApplicationEvent {\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    public UserRegisterEvent(Object source) {\n        super(source);\n    }\n\n    public UserRegisterEvent(Object source, String username) {\n        super(source);\n        this.username = username;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n}\n"
  },
  {
    "path": "lab-54/lab-54-demo/src/main/java/cn/iocoder/springboot/lab54/eventdemo/service/CouponService.java",
    "content": "package cn.iocoder.springboot.lab54.eventdemo.service;\n\nimport cn.iocoder.springboot.lab54.eventdemo.event.UserRegisterEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class CouponService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @EventListener\n    public void addCoupon(UserRegisterEvent event) {\n        logger.info(\"[addCoupon][给用户({}) 发放优惠劵]\", event.getUsername());\n    }\n\n}\n"
  },
  {
    "path": "lab-54/lab-54-demo/src/main/java/cn/iocoder/springboot/lab54/eventdemo/service/EmailService.java",
    "content": "package cn.iocoder.springboot.lab54.eventdemo.service;\n\nimport cn.iocoder.springboot.lab54.eventdemo.event.UserRegisterEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.scheduling.annotation.Async;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class EmailService implements ApplicationListener<UserRegisterEvent> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    @Async\n    public void onApplicationEvent(UserRegisterEvent event) {\n        logger.info(\"[onApplicationEvent][给用户({}) 发送邮件]\", event.getUsername());\n    }\n\n}\n"
  },
  {
    "path": "lab-54/lab-54-demo/src/main/java/cn/iocoder/springboot/lab54/eventdemo/service/UserService.java",
    "content": "package cn.iocoder.springboot.lab54.eventdemo.service;\n\nimport cn.iocoder.springboot.lab54.eventdemo.event.UserRegisterEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.ApplicationEventPublisherAware;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class UserService implements ApplicationEventPublisherAware {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    private ApplicationEventPublisher applicationEventPublisher;\n\n    @Override\n    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {\n        this.applicationEventPublisher = applicationEventPublisher;\n    }\n\n    public void register(String username) {\n        // ... 执行注册逻辑\n        logger.info(\"[register][执行用户({}) 的注册逻辑]\", username);\n\n        // ... 发布\n        applicationEventPublisher.publishEvent(new UserRegisterEvent(this, username));\n    }\n\n}\n"
  },
  {
    "path": "lab-54/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-54</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-54-demo</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-54/《芋道 Spring Boot 事件机制 Event 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Event/?github>\n"
  },
  {
    "path": "lab-55/lab-55-mapstruct-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-55</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-55-mapstruct-demo</artifactId>\n\n    <properties>\n        <java.version>1.8</java.version>\n        <mapstruct.version>1.3.1.Final</mapstruct.version>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>org.mapstruct</groupId>\n            <artifactId>mapstruct</artifactId>\n            <version>${mapstruct.version}</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.8.1</version>\n                <configuration>\n                    <source>${java.version}</source>\n                    <target>${java.version}</target>\n                    <annotationProcessorPaths>\n                        <path>\n                            <groupId>org.mapstruct</groupId>\n                            <artifactId>mapstruct-processor</artifactId>\n                            <version>${mapstruct.version}</version>\n                        </path>\n                    </annotationProcessorPaths>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-55/lab-55-mapstruct-demo/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/UserBOTest.java",
    "content": "package cn.iocoder.springboot.lab55.mapstructdemo;\n\nimport cn.iocoder.springboot.lab55.mapstructdemo.bo.UserBO;\nimport cn.iocoder.springboot.lab55.mapstructdemo.convert.UserConvert;\nimport cn.iocoder.springboot.lab55.mapstructdemo.dataobject.UserDO;\n\npublic class UserBOTest {\n\n    public static void main(String[] args) {\n        // 创建 UserDO 对象\n        UserDO userDO = new UserDO()\n                .setId(1).setUsername(\"yudaoyuanma\").setPassword(\"buzhidao\");\n        // 进行转换\n        UserBO userBO = UserConvert.INSTANCE.convert(userDO);\n        System.out.println(userBO.getId());\n        System.out.println(userBO.getUsername());\n        System.out.println(userBO.getPassword());\n    }\n\n}\n"
  },
  {
    "path": "lab-55/lab-55-mapstruct-demo/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/UserDetailBOTest.java",
    "content": "package cn.iocoder.springboot.lab55.mapstructdemo;\n\nimport cn.iocoder.springboot.lab55.mapstructdemo.bo.UserDetailBO;\nimport cn.iocoder.springboot.lab55.mapstructdemo.convert.UserConvert;\nimport cn.iocoder.springboot.lab55.mapstructdemo.dataobject.UserDO;\n\npublic class UserDetailBOTest {\n\n    public static void main(String[] args) {\n        // 创建 UserDO 对象\n        UserDO userDO = new UserDO()\n                .setId(1).setUsername(\"yudaoyuanma\").setPassword(\"buzhidao\");\n        // 进行转换\n        UserDetailBO userDetailBO = UserConvert.INSTANCE.convertDetail(userDO);\n        System.out.println(userDetailBO.getUserId());\n    }\n\n}\n"
  },
  {
    "path": "lab-55/lab-55-mapstruct-demo/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/bo/UserBO.java",
    "content": "package cn.iocoder.springboot.lab55.mapstructdemo.bo;\n\npublic class UserBO {\n\n    /** 用户编号 **/\n    private Integer id;\n    /** 用户名 **/\n    private String username;\n    /** 密码 **/\n    private String password;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserBO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserBO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserBO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-55/lab-55-mapstruct-demo/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/bo/UserDetailBO.java",
    "content": "package cn.iocoder.springboot.lab55.mapstructdemo.bo;\n\npublic class UserDetailBO {\n\n    private Integer userId;\n\n    public Integer getUserId() {\n        return userId;\n    }\n\n    public UserDetailBO setUserId(Integer userId) {\n        this.userId = userId;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-55/lab-55-mapstruct-demo/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/convert/UserConvert.java",
    "content": "package cn.iocoder.springboot.lab55.mapstructdemo.convert;\n\nimport cn.iocoder.springboot.lab55.mapstructdemo.bo.UserBO;\nimport cn.iocoder.springboot.lab55.mapstructdemo.bo.UserDetailBO;\nimport cn.iocoder.springboot.lab55.mapstructdemo.dataobject.UserDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.Mapping;\nimport org.mapstruct.Mappings;\nimport org.mapstruct.factory.Mappers;\n\n@Mapper\npublic interface UserConvert {\n\n    UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);\n\n    UserBO convert(UserDO userDO);\n\n    @Mappings({\n            @Mapping(source = \"id\", target = \"userId\")\n    })\n    UserDetailBO convertDetail(UserDO userDO);\n\n}\n"
  },
  {
    "path": "lab-55/lab-55-mapstruct-demo/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab55.mapstructdemo.dataobject;\n\n/**\n * 用户 DO\n */\npublic class UserDO {\n\n    /** 用户编号 **/\n    private Integer id;\n    /** 用户名 **/\n    private String username;\n    /** 密码 **/\n    private String password;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-55/lab-55-mapstruct-demo-lombok/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-55</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-55-mapstruct-demo-lombok</artifactId>\n\n    <properties>\n        <java.version>1.8</java.version>\n        <mapstruct.version>1.3.1.Final</mapstruct.version>\n        <lombok.version>1.18.12</lombok.version>\n    </properties>\n\n    <dependencies>\n        <!-- 引入 mapstruct 依赖 -->\n        <dependency>\n            <groupId>org.mapstruct</groupId>\n            <artifactId>mapstruct</artifactId>\n            <version>${mapstruct.version}</version>\n        </dependency>\n\n        <!-- 引入 lombok 依赖 -->\n        <dependency>\n            <groupId>org.projectlombok</groupId>\n            <artifactId>lombok</artifactId>\n            <version>${lombok.version}</version>\n            <scope>provided</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>3.8.1</version>\n                <configuration>\n                    <source>${java.version}</source>\n                    <target>${java.version}</target>\n                    <annotationProcessorPaths>\n                        <!-- 引入 mapstruct-processor -->\n                        <path>\n                            <groupId>org.mapstruct</groupId>\n                            <artifactId>mapstruct-processor</artifactId>\n                            <version>${mapstruct.version}</version>\n                        </path>\n                        <!-- 引入 lombok-processor -->\n                        <path>\n                            <groupId>org.projectlombok</groupId>\n                            <artifactId>lombok</artifactId>\n                            <version>${lombok.version}</version>\n                        </path>\n                    </annotationProcessorPaths>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-55/lab-55-mapstruct-demo-lombok/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/UserBOTest.java",
    "content": "package cn.iocoder.springboot.lab55.mapstructdemo;\n\nimport cn.iocoder.springboot.lab55.mapstructdemo.bo.UserBO;\nimport cn.iocoder.springboot.lab55.mapstructdemo.convert.UserConvert;\nimport cn.iocoder.springboot.lab55.mapstructdemo.dataobject.UserDO;\n\npublic class UserBOTest {\n\n    public static void main(String[] args) {\n        // 创建 UserDO 对象\n        UserDO userDO = new UserDO()\n                .setId(1).setUsername(\"yudaoyuanma\").setPassword(\"buzhidao\");\n        // 进行转换\n        UserBO userBO = UserConvert.INSTANCE.convert(userDO);\n        System.out.println(userBO.getId());\n        System.out.println(userBO.getUsername());\n        System.out.println(userBO.getPassword());\n    }\n\n}\n"
  },
  {
    "path": "lab-55/lab-55-mapstruct-demo-lombok/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/bo/UserBO.java",
    "content": "package cn.iocoder.springboot.lab55.mapstructdemo.bo;\n\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\n@Data\n@Accessors(chain = true)\npublic class UserBO {\n\n    /** 用户编号 **/\n    private Integer id;\n    /** 用户名 **/\n    private String username;\n    /** 密码 **/\n    private String password;\n\n}\n"
  },
  {
    "path": "lab-55/lab-55-mapstruct-demo-lombok/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/convert/UserConvert.java",
    "content": "package cn.iocoder.springboot.lab55.mapstructdemo.convert;\n\nimport cn.iocoder.springboot.lab55.mapstructdemo.bo.UserBO;\nimport cn.iocoder.springboot.lab55.mapstructdemo.dataobject.UserDO;\nimport org.mapstruct.Mapper;\nimport org.mapstruct.factory.Mappers;\n\n@Mapper\npublic interface UserConvert {\n\n    UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);\n\n    UserBO convert(UserDO userDO);\n\n}\n"
  },
  {
    "path": "lab-55/lab-55-mapstruct-demo-lombok/src/main/java/cn/iocoder/springboot/lab55/mapstructdemo/dataobject/UserDO.java",
    "content": "package cn.iocoder.springboot.lab55.mapstructdemo.dataobject;\n\n\nimport lombok.Data;\nimport lombok.experimental.Accessors;\n\n@Data\n@Accessors(chain = true)\npublic class UserDO {\n\n    /** 用户编号 **/\n    private Integer id;\n    /** 用户名 **/\n    private String username;\n    /** 密码 **/\n    private String password;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n    public UserDO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public UserDO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-55/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-55</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-55-mapstruct-demo</module>\n        <module>lab-55-mapstruct-demo-lombok</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-55/《芋道 Spring Boot 对象转换 MapStruct 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/MapStruct/?github>\n"
  },
  {
    "path": "lab-56/lab-56-demo01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.2.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-56-demo01</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-56/lab-56-demo01/src/main/java/cn/iocoder/springboot/lab56/Demo01Application.java",
    "content": "package cn.iocoder.springboot.lab56;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Demo01Application {\n\n    public static void main(String[] args) {\n        System.setProperty(\"server.port\", \"18080\"); // 端口 18080\n        SpringApplication.run(Demo01Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-56/lab-56-demo01/src/main/java/cn/iocoder/springboot/lab56/Demo02Application.java",
    "content": "package cn.iocoder.springboot.lab56;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Demo02Application {\n\n    public static void main(String[] args) {\n        System.setProperty(\"server.port\", \"28080\");  // 端口 28080\n        SpringApplication.run(Demo02Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-56/lab-56-demo01/src/main/java/cn/iocoder/springboot/lab56/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab56.controller;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Value(\"${server.port}\")\n    private Integer serverPort;\n\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return \"echo:\" + serverPort;\n    }\n\n}\n"
  },
  {
    "path": "lab-56/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-56</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-56-demo01</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-56/《芋道 APISIX 极简入门（国产微服务网关）》.md",
    "content": "<http://www.iocoder.cn/APISIX/install/?github>\n"
  },
  {
    "path": "lab-56/《芋道 Kong 极简入门（微服务网关）》.md",
    "content": "<http://www.iocoder.cn/Kong/install/?github>\n"
  },
  {
    "path": "lab-57/lab-57-hystrix-demo01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-57</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-57-hystrix-demo01</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Hystrix 依赖 -->\n        <dependency>\n            <groupId>com.netflix.hystrix</groupId>\n            <artifactId>hystrix-core</artifactId>\n            <version>1.5.18</version>\n        </dependency>\n        <dependency>\n            <groupId>com.netflix.hystrix</groupId>\n            <artifactId>hystrix-javanica</artifactId>\n            <version>1.5.18</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-57/lab-57-hystrix-demo01/src/main/java/cn/iocoder/springboot/lab57/hystrixdemo/DemoApplication.java",
    "content": "package cn.iocoder.springboot.lab57.hystrixdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    @Bean\n    public RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-57/lab-57-hystrix-demo01/src/main/java/cn/iocoder/springboot/lab57/hystrixdemo/config/HystrixConfig.java",
    "content": "package cn.iocoder.springboot.lab57.hystrixdemo.config;\n\nimport com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.EnableAspectJAutoProxy;\n\n@Configuration\n@EnableAspectJAutoProxy // 开启 AOP 代理的支持\npublic class HystrixConfig {\n\n    @Bean\n    public HystrixCommandAspect hystrixCommandAspect() {\n        return new HystrixCommandAspect();\n    }\n\n}\n"
  },
  {
    "path": "lab-57/lab-57-hystrix-demo01/src/main/java/cn/iocoder/springboot/lab57/hystrixdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab57.hystrixdemo.controller;\n\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @GetMapping(\"/get_user\")\n    @HystrixCommand(fallbackMethod = \"getUserFallback\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        logger.info(\"[getUser][准备调用 user-service 获取用户({})详情]\", id);\n        return restTemplate.getForEntity(\"http://127.0.0.1:18080/user/get?id=\" + id, String.class).getBody();\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, ExceptionUtils.getRootCauseMessage(throwable));\n        return \"mock:User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-application/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-57-hystrix-dubbo-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-57-hystrix-dubbo-demo-application</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入用户服务 API 包 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-57-hystrix-dubbo-demo-user-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Dubbo 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n        <!-- 使用 Nacos 作为注册中心 -->\n        <dependency>\n            <groupId>com.alibaba.nacos</groupId>\n            <artifactId>nacos-client</artifactId>\n            <version>1.2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-registry-nacos</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n        <!-- 引入 Hystrix 依赖 -->\n        <dependency>\n            <groupId>com.netflix.hystrix</groupId>\n            <artifactId>hystrix-core</artifactId>\n            <version>1.5.18</version>\n        </dependency>\n        <dependency>\n            <groupId>com.netflix.hystrix</groupId>\n            <artifactId>hystrix-javanica</artifactId>\n            <version>1.5.18</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-application/src/main/java/cn/iocoder/springcloud/labx23/demo/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx23.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-application/src/main/java/cn/iocoder/springcloud/labx23/demo/config/HystrixConfig.java",
    "content": "package cn.iocoder.springcloud.labx23.demo.config;\n\nimport com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.EnableAspectJAutoProxy;\n\n@Configuration\n@EnableAspectJAutoProxy // 开启 AOP 代理的支持\npublic class HystrixConfig {\n\n    @Bean\n    public HystrixCommandAspect hystrixCommandAspect() {\n        return new HystrixCommandAspect();\n    }\n\n}\n"
  },
  {
    "path": "lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-application/src/main/java/cn/iocoder/springcloud/labx23/demo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx23.demo.controller;\n\nimport cn.iocoder.springboot.lab57.userservice.api.UserService;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Reference(protocol = \"dubbo\", version = \"1.0.0\")\n    private UserService userService;\n\n    @GetMapping(\"/get_user\")\n    @HystrixCommand(fallbackMethod = \"getUserFallback\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        logger.info(\"[getUser][准备调用 user-service 获取用户({})详情]\", id);\n        return userService.getUser(id);\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, ExceptionUtils.getRootCauseMessage(throwable));\n        return \"mock:User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-application/src/main/resources/application.yaml",
    "content": "# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: demo-consumer # 应用名\n  # Dubbo 注册中心配置\n  registry:\n    address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 消费者配置\n  consumer:\n    timeout: 1000 # 【重要】远程服务调用超时时间，单位：毫秒。默认为 1000 毫秒，胖友可以根据自己业务修改\n    UserRpcService:\n      version: 1.0.0\n"
  },
  {
    "path": "lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-user-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-57-hystrix-dubbo-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-57-hystrix-dubbo-demo-user-service</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入用户服务 API 包 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-57-hystrix-dubbo-demo-user-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Boot 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对 Dubbo 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n        <!-- 使用 Nacos 作为注册中心 -->\n        <dependency>\n            <groupId>com.alibaba.nacos</groupId>\n            <artifactId>nacos-client</artifactId>\n            <version>1.2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-registry-nacos</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-user-service/src/main/java/cn/iocoder/springboot/lab57/userservice/UserServiceApplication.java",
    "content": "package cn.iocoder.springboot.lab57.userservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserServiceApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-user-service/src/main/java/cn/iocoder/springboot/lab57/userservice/service/UserServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab57.userservice.service;\n\nimport cn.iocoder.springboot.lab57.userservice.api.UserService;\n\n@org.apache.dubbo.config.annotation.Service(version = \"1.0.0\")\npublic class UserServiceImpl implements UserService {\n\n    @Override\n    public String getUser(Integer id) {\n        return \"User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-user-service/src/main/resources/application.yaml",
    "content": "# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: user-service # 应用名\n  # Dubbo 注册中心配\n  registry:\n    address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # Dubbo 服务提供者配置\n  provider:\n    timeout: 1000 # 【重要】远程服务调用超时时间，单位：毫秒。默认为 1000 毫秒，胖友可以根据自己业务修改\n    UserRpcService:\n      version: 1.0.0\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springboot.lab57.userservice.service\n"
  },
  {
    "path": "lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-user-service-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-57-hystrix-dubbo-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-57-hystrix-dubbo-demo-user-service-api</artifactId>\n\n</project>\n"
  },
  {
    "path": "lab-57/lab-57-hystrix-dubbo-demo/lab-57-hystrix-dubbo-demo-user-service-api/src/main/java/cn/iocoder/springboot/lab57/userservice/api/UserService.java",
    "content": "package cn.iocoder.springboot.lab57.userservice.api;\n\npublic interface UserService {\n\n    String getUser(Integer id);\n\n}\n"
  },
  {
    "path": "lab-57/lab-57-hystrix-dubbo-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-57</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-57-hystrix-dubbo-demo</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>lab-57-hystrix-dubbo-demo-user-service</module>\n        <module>lab-57-hystrix-dubbo-demo-user-service-api</module>\n        <module>lab-57-hystrix-dubbo-demo-application</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-57/lab-57-user-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-57</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-57-user-service</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-57/lab-57-user-service/src/main/java/cn/iocoder/springboot/lab57/userservice/UserServiceApplication.java",
    "content": "package cn.iocoder.springboot.lab57.userservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    @RestController\n    @RequestMapping(\"/user\")\n    public class UserController {\n\n        @GetMapping(\"/get\")\n        public String get(@RequestParam(\"id\") Integer id) {\n            return \"User:\" + id;\n        }\n\n        @GetMapping(\"/batch_get\")\n        public List<String> batchGet(@RequestParam(\"ids\") List<Integer> ids) {\n            return ids.stream().map(id -> \"User:\" + id).collect(Collectors.toList());\n        }\n\n    }\n\n    public static void main(String[] args) {\n        // 设置端口\n        System.setProperty(\"server.port\", \"18080\");\n\n        // 应用启动\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-57/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-57</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-57-user-service</module>\n        <module>lab-57-hystrix-demo01</module>\n        <module>lab-57-hystrix-dubbo-demo</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-57/《芋道 Spring Boot 服务容错 Hystrix 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Hystrix/?github>\n"
  },
  {
    "path": "lab-58/lab-58-feign-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-58</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-58-feign-demo</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Feign 相关依赖 -->\n        <dependency>\n            <groupId>io.github.openfeign</groupId>\n            <artifactId>feign-core</artifactId>\n            <version>11.0</version>\n        </dependency>\n        <!-- 引入 Feign GSON 拓展的依赖 -->\n        <dependency>\n            <groupId>io.github.openfeign</groupId>\n            <artifactId>feign-gson</artifactId>\n            <version>11.0</version>\n        </dependency>\n        <!-- 引入 Feign SpringMVC 拓展的依赖 -->\n        <dependency>\n            <groupId>io.github.openfeign</groupId>\n            <artifactId>feign-spring4</artifactId>\n            <version>11.0</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-58/lab-58-feign-demo/src/main/java/cn/iocoder/springboot/lab58/feigndemo/FeignDemoApplication.java",
    "content": "package cn.iocoder.springboot.lab58.feigndemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class FeignDemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(FeignDemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-58/lab-58-feign-demo/src/main/java/cn/iocoder/springboot/lab58/feigndemo/config/FeignConfig.java",
    "content": "package cn.iocoder.springboot.lab58.feigndemo.config;\n\nimport cn.iocoder.springboot.lab58.feigndemo.feign.UserServiceFeignClient;\nimport cn.iocoder.springboot.lab58.feigndemo.feign.UserServiceFeignClient02;\nimport feign.Feign;\nimport feign.gson.GsonDecoder;\nimport feign.gson.GsonEncoder;\nimport feign.spring.SpringContract;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class FeignConfig {\n\n    @Bean\n    public UserServiceFeignClient userServiceFeignClient() {\n        return Feign.builder()\n                .encoder(new GsonEncoder())\n                .decoder(new GsonDecoder())\n                .target(UserServiceFeignClient.class, \"http://127.0.0.1:18080\"); // 目标地址\n    }\n\n    @Bean\n    public UserServiceFeignClient02 userServiceFeignClient02() {\n        return Feign.builder()\n                .encoder(new GsonEncoder())\n                .decoder(new GsonDecoder())\n                .contract(new SpringContract())\n                .target(UserServiceFeignClient02.class, \"http://127.0.0.1:18080\"); // 目标地址\n    }\n\n}\n"
  },
  {
    "path": "lab-58/lab-58-feign-demo/src/main/java/cn/iocoder/springboot/lab58/feigndemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab58.feigndemo.controller;\n\nimport cn.iocoder.springboot.lab58.feigndemo.feign.UserServiceFeignClient;\nimport cn.iocoder.springboot.lab58.feigndemo.feign.request.UserAddRequest;\nimport cn.iocoder.springboot.lab58.feigndemo.feign.response.UserResponse;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private UserServiceFeignClient userServiceFeignClient;\n\n    @GetMapping(\"/test01\")\n    public UserResponse test01() {\n        return userServiceFeignClient.get(1);\n//        System.out.println(\"编号：\" + user.getId());\n//        System.out.println(\"昵称：\" + user.getName());\n//        System.out.println(\"性别：\" + user.getGender());\n    }\n\n    @GetMapping(\"/test02A\")\n    public List<UserResponse> test02A() {\n        return userServiceFeignClient.list(\"你猜\", 1);\n    }\n\n    @GetMapping(\"/test02B\")\n    public List<UserResponse> test02B() {\n        Map<String, Object> queryMap = new HashMap<>();\n        queryMap.put(\"name\", \"昵称\");\n        return userServiceFeignClient.list(queryMap);\n    }\n\n    @GetMapping(\"/test03\")\n    public Integer test03() {\n        return userServiceFeignClient.add(new UserAddRequest()\n            .setName(\"昵称\").setGender(1));\n    }\n\n}\n"
  },
  {
    "path": "lab-58/lab-58-feign-demo/src/main/java/cn/iocoder/springboot/lab58/feigndemo/controller/DemoController02.java",
    "content": "package cn.iocoder.springboot.lab58.feigndemo.controller;\n\nimport cn.iocoder.springboot.lab58.feigndemo.feign.UserServiceFeignClient02;\nimport cn.iocoder.springboot.lab58.feigndemo.feign.request.UserAddRequest;\nimport cn.iocoder.springboot.lab58.feigndemo.feign.response.UserResponse;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.List;\n\n@RestController\n@RequestMapping(\"/demo02\")\npublic class DemoController02 {\n\n    @Autowired\n    private UserServiceFeignClient02 userServiceFeignClient;\n\n    @GetMapping(\"/test01\")\n    public UserResponse test01() {\n        return userServiceFeignClient.get(1);\n//        System.out.println(\"编号：\" + user.getId());\n//        System.out.println(\"昵称：\" + user.getName());\n//        System.out.println(\"性别：\" + user.getGender());\n    }\n\n    @GetMapping(\"/test02A\")\n    public List<UserResponse> test02A() {\n        return userServiceFeignClient.list(\"你猜\", null);\n    }\n\n//    @GetMapping(\"/test02B\")\n//    public List<UserResponse> test02B() {\n//        Map<String, Object> queryMap = new HashMap<>();\n//        queryMap.put(\"name\", \"昵称\");\n//        return userServiceFeignClient.list(queryMap);\n//    }\n\n    @GetMapping(\"/test03\")\n    public Integer test03() {\n        return userServiceFeignClient.add(new UserAddRequest()\n            .setName(\"昵称\").setGender(1));\n    }\n\n}\n"
  },
  {
    "path": "lab-58/lab-58-feign-demo/src/main/java/cn/iocoder/springboot/lab58/feigndemo/feign/UserServiceFeignClient.java",
    "content": "package cn.iocoder.springboot.lab58.feigndemo.feign;\n\nimport cn.iocoder.springboot.lab58.feigndemo.feign.request.UserAddRequest;\nimport cn.iocoder.springboot.lab58.feigndemo.feign.response.UserResponse;\nimport feign.*;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * 基于 Contract.Default 默认契约\n */\npublic interface UserServiceFeignClient {\n\n    // 获得用户详情\n    @RequestLine(\"GET /user/get?id={id}\")\n    UserResponse get(@Param(\"id\") Integer id);\n\n    @RequestLine(\"GET /user/list?name={name}&gender={gender}\")\n    List<UserResponse> list(@Param(\"name\") String name,\n                            @Param(\"gender\") Integer gender);\n\n    @RequestLine(\"GET /user/list\")\n    List<UserResponse> list(@QueryMap Map<String, Object> queryMap);\n\n    @RequestLine(\"POST /user/add\")\n    @Headers(\"Content-Type: application/json\")\n    Integer add(UserAddRequest request);\n\n}\n"
  },
  {
    "path": "lab-58/lab-58-feign-demo/src/main/java/cn/iocoder/springboot/lab58/feigndemo/feign/UserServiceFeignClient02.java",
    "content": "package cn.iocoder.springboot.lab58.feigndemo.feign;\n\nimport cn.iocoder.springboot.lab58.feigndemo.feign.request.UserAddRequest;\nimport cn.iocoder.springboot.lab58.feigndemo.feign.response.UserResponse;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestParam;\n\nimport java.util.List;\n\n/**\n * 基于 SpringContract 拓展契约\n */\npublic interface UserServiceFeignClient02 {\n\n    // 获得用户详情\n    @GetMapping(\"/user/get\")\n    UserResponse get(@RequestParam(\"id\") Integer id);\n\n    @GetMapping(\"/user/list\")\n    List<UserResponse> list(@RequestParam(\"name\") String name,\n                            @RequestParam(\"gender\") Integer gender);\n\n//    @RequestLine(\"GET /user/list\")\n//    List<UserResponse> list(@QueryMap Map<String, Object> queryMap);\n\n    @PostMapping(value = \"/user/add\", consumes = \"application/json\")\n    Integer add(@RequestBody UserAddRequest request);\n\n}\n"
  },
  {
    "path": "lab-58/lab-58-feign-demo/src/main/java/cn/iocoder/springboot/lab58/feigndemo/feign/request/UserAddRequest.java",
    "content": "package cn.iocoder.springboot.lab58.feigndemo.feign.request;\n\n/**\n * User 添加 Request\n */\npublic class UserAddRequest {\n\n    private String name;\n    private Integer gender;\n\n    public String getName() {\n        return name;\n    }\n\n    public UserAddRequest setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserAddRequest setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-58/lab-58-feign-demo/src/main/java/cn/iocoder/springboot/lab58/feigndemo/feign/response/UserResponse.java",
    "content": "package cn.iocoder.springboot.lab58.feigndemo.feign.response;\n\npublic class UserResponse {\n\n    private Integer id;\n    private String name;\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserResponse setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserResponse setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserResponse setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-58/lab-58-user-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-58</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-58-user-service</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-58/lab-58-user-service/src/main/java/cn/iocoder/springboot/lab58/userservice/UserServiceApplication.java",
    "content": "package cn.iocoder.springboot.lab58.userservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) {\n        // 设置端口\n        System.setProperty(\"server.port\", \"18080\");\n\n        // 应用启动\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-58/lab-58-user-service/src/main/java/cn/iocoder/springboot/lab58/userservice/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab58.userservice.controller;\n\nimport cn.iocoder.springboot.lab58.userservice.request.UserAddRequest;\nimport cn.iocoder.springboot.lab58.userservice.response.UserResponse;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @GetMapping(\"/get\") // 获得指定用户\n    public UserResponse get(@RequestParam(\"id\") Integer id) {\n        return new UserResponse().setId(id)\n                .setName(\"昵称：\" + id).setGender(id % 2 == 0 ? 1 : 2);\n    }\n\n    @GetMapping(\"/list\") // 获得匹配的用户列表\n    public List<UserResponse> list(@RequestParam(value = \"name\", required = false) String name,\n                                   @RequestParam(value = \"gender\", required = false) Integer gender) {\n        List<UserResponse> users = new ArrayList<>();\n        for (int id = 1; id <= 3; id++) {\n            users.add(new UserResponse().setId(id)\n                .setName(name + \"_\" + id).setGender(gender));\n        }\n        return users;\n    }\n\n    @PostMapping(\"/add\") // 添加用户\n    public Integer add(@RequestBody UserAddRequest request) {\n        System.out.println(\"昵称：\" + request.getName());\n        System.out.println(\"性别：\" + request.getGender());\n        return 1;\n    }\n\n}\n"
  },
  {
    "path": "lab-58/lab-58-user-service/src/main/java/cn/iocoder/springboot/lab58/userservice/request/UserAddRequest.java",
    "content": "package cn.iocoder.springboot.lab58.userservice.request;\n\n/**\n * User 添加 Request\n */\npublic class UserAddRequest {\n\n    private String name;\n    private Integer gender;\n\n    public String getName() {\n        return name;\n    }\n\n    public UserAddRequest setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserAddRequest setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-58/lab-58-user-service/src/main/java/cn/iocoder/springboot/lab58/userservice/response/UserResponse.java",
    "content": "package cn.iocoder.springboot.lab58.userservice.response;\n\n/**\n * User 信息 Response\n */\npublic class UserResponse {\n\n    private Integer id;\n    private String name;\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserResponse setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserResponse setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserResponse setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-58/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-58</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>lab-58-user-service</module>\n        <module>lab-58-feign-demo</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-58/《芋道 Spring Boot 声明式调用 Feign 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Feign/?github>\n"
  },
  {
    "path": "lab-59/lab-59-resilience4j-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-59</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-59-resilience4j-actuator</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Resilience4j Starter 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>io.github.resilience4j</groupId>\n            <artifactId>resilience4j-spring-boot2</artifactId>\n            <version>1.4.0</version>\n        </dependency>\n\n        <!-- 引入 Aspectj 依赖，支持 AOP 相关注解、表达式等等 -->\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjrt</artifactId>\n            <version>1.9.5</version>\n        </dependency>\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjweaver</artifactId>\n            <version>1.9.5</version>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-59/lab-59-resilience4j-actuator/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/DemoApplication.java",
    "content": "package cn.iocoder.springboot.lab59.resillience4jdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    @Bean\n    public RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-59/lab-59-resilience4j-actuator/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/BulkheadDemoController.java",
    "content": "package cn.iocoder.springboot.lab59.resillience4jdemo.controller;\n\nimport io.github.resilience4j.bulkhead.annotation.Bulkhead;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/bulkhead-demo\")\npublic class BulkheadDemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/get_user\")\n    @Bulkhead(name = \"backendC\", fallbackMethod = \"getUserFallback\", type = Bulkhead.Type.SEMAPHORE)\n    public String getUser(@RequestParam(\"id\") Integer id) throws InterruptedException {\n        logger.info(\"[getUser][id({})]\", id);\n        Thread.sleep(10 * 1000L); // sleep 10 秒\n        return \"User:\" + id;\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, throwable.getClass().getSimpleName());\n        return \"mock:User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "lab-59/lab-59-resilience4j-actuator/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab59.resillience4jdemo.controller;\n\nimport io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @GetMapping(\"/get_user\")\n    @CircuitBreaker(name = \"backendA\", fallbackMethod = \"getUserFallback\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        logger.info(\"[getUser][准备调用 user-service 获取用户({})详情]\", id);\n        return restTemplate.getForEntity(\"http://127.0.0.1:18080/user/get?id=\" + id, String.class).getBody();\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, throwable.getClass().getSimpleName());\n        return \"mock:User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "lab-59/lab-59-resilience4j-actuator/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/RateLimiterDemoController.java",
    "content": "package cn.iocoder.springboot.lab59.resillience4jdemo.controller;\n\nimport io.github.resilience4j.ratelimiter.annotation.RateLimiter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/rate-limiter-demo\")\npublic class RateLimiterDemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/get_user\")\n    @RateLimiter(name = \"backendB\", fallbackMethod = \"getUserFallback\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        return \"User:\" + id;\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, throwable.getClass().getSimpleName());\n        return \"mock:User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "lab-59/lab-59-resilience4j-actuator/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/RetryDemoController.java",
    "content": "package cn.iocoder.springboot.lab59.resillience4jdemo.controller;\n\nimport io.github.resilience4j.retry.annotation.Retry;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@RestController\n@RequestMapping(\"/retry-demo\")\npublic class RetryDemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @GetMapping(\"/get_user\")\n    @Retry(name = \"backendE\", fallbackMethod = \"getUserFallback\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        logger.info(\"[getUser][准备调用 user-service 获取用户({})详情]\", id);\n        return restTemplate.getForEntity(\"http://127.0.0.1:18080/user/get?id=\" + id, String.class).getBody();\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, throwable.getClass().getSimpleName());\n        return \"mock:User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "lab-59/lab-59-resilience4j-actuator/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/ThreadPoolBulkheadDemoController.java",
    "content": "package cn.iocoder.springboot.lab59.resillience4jdemo.controller;\n\nimport io.github.resilience4j.bulkhead.annotation.Bulkhead;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\n\n@RestController\n@RequestMapping(\"/thread-pool-bulkhead-demo\")\npublic class ThreadPoolBulkheadDemoController {\n\n    @Autowired\n    private ThreadPoolBulkheadService threadPoolBulkheadService;\n\n    @GetMapping(\"/get_user\")\n    public String getUser(@RequestParam(\"id\") Integer id) throws ExecutionException, InterruptedException {\n        threadPoolBulkheadService.getUser0(id);\n        return threadPoolBulkheadService.getUser0(id).get();\n    }\n\n    @Service\n    public static class ThreadPoolBulkheadService {\n\n        private Logger logger = LoggerFactory.getLogger(ThreadPoolBulkheadService.class);\n\n        @Bulkhead(name = \"backendD\", fallbackMethod = \"getUserFallback\", type = Bulkhead.Type.THREADPOOL)\n        public CompletableFuture<String> getUser0(Integer id) throws InterruptedException {\n            logger.info(\"[getUser][id({})]\", id);\n            Thread.sleep(10 * 1000L); // sleep 10 秒\n            return CompletableFuture.completedFuture(\"User:\" + id);\n        }\n\n        public CompletableFuture<String> getUserFallback(Integer id, Throwable throwable) {\n            logger.info(\"[getUserFallback][id({}) exception({})]\", id, throwable.getClass().getSimpleName());\n            return CompletableFuture.completedFuture(\"mock:User:\" + id);\n        }\n\n    }\n\n\n}\n"
  },
  {
    "path": "lab-59/lab-59-resilience4j-actuator/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/TimeLimiterDemoController.java",
    "content": "package cn.iocoder.springboot.lab59.resillience4jdemo.controller;\n\nimport io.github.resilience4j.bulkhead.annotation.Bulkhead;\nimport io.github.resilience4j.timelimiter.annotation.TimeLimiter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\n\n@RestController\n@RequestMapping(\"/time-limiter-demo\")\npublic class TimeLimiterDemoController {\n\n    @Autowired\n    private TimeLimiterService timeLimiterService;\n\n    @GetMapping(\"/get_user\")\n    public String getUser(@RequestParam(\"id\") Integer id) throws ExecutionException, InterruptedException {\n        return timeLimiterService.getUser0(id).get();\n    }\n\n    @Service\n    public static class TimeLimiterService {\n\n        private Logger logger = LoggerFactory.getLogger(TimeLimiterService.class);\n\n        @Bulkhead(name = \"backendD\", type = Bulkhead.Type.THREADPOOL)\n        @TimeLimiter(name = \"backendF\", fallbackMethod = \"getUserFallback\")\n        public CompletableFuture<String> getUser0(Integer id) throws InterruptedException {\n            logger.info(\"[getUser][id({})]\", id);\n            Thread.sleep(10 * 1000L); // sleep 10 秒\n            return CompletableFuture.completedFuture(\"User:\" + id);\n        }\n\n        public CompletableFuture<String> getUserFallback(Integer id, Throwable throwable) {\n            logger.info(\"[getUserFallback][id({}) exception({})]\", id, throwable.getClass().getSimpleName());\n            return CompletableFuture.completedFuture(\"mock:User:\" + id);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-59/lab-59-resilience4j-actuator/src/main/resources/application.yml",
    "content": "resilience4j:\n  # Resilience4j 的断路器配置项，对应 CircuitBreakerProperties 属性类\n  circuitbreaker:\n    instances:\n      backendA:\n        failure-rate-threshold: 50 # 熔断器关闭状态和半开状态使用的同一个失败率阈值，单位：百分比。默认为 50\n        ring-buffer-size-in-closed-state: 5 # 熔断器关闭状态的缓冲区大小，不会限制线程的并发量，在熔断器发生状态转换前所有请求都会调用后端服务。默认为 100\n        ring-buffer-size-in-half-open-state: 5 # 熔断器半开状态的缓冲区大小，会限制线程的并发量。例如，缓冲区为 10 则每次只会允许 10 个请求调用后端服务。默认为 10\n        wait-duration-in-open-state : 5000 # 熔断器从打开状态转变为半开状态等待的时间，单位：微秒\n        automatic-transition-from-open-to-half-open-enabled: true # 如果置为 true，当等待时间结束会自动由打开变为半开；若置为 false，则需要一个请求进入来触发熔断器状态转换。默认为 true\n        register-health-indicator: true # 是否注册到健康监测\n\n  # Resilience4j 的限流器配置项，对应 RateLimiterProperties 属性类\n  ratelimiter:\n    instances:\n      backendB:\n        limit-for-period: 1 # 每个周期内，允许的请求数。默认为 50\n        limit-refresh-period: 10s # 每个周期的时长，单位：微秒。默认为 500\n        timeout-duration: 5s # 被限流时，阻塞等待的时长，单位：微秒。默认为 5s\n        register-health-indicator: true # 是否注册到健康监测\n\n  # Resilience4j 的信号量 Bulkhead 配置项，对应 BulkheadConfigurationProperties 属性类\n  bulkhead:\n    instances:\n      backendC:\n        max-concurrent-calls: 1 # 并发调用数。默认为 25\n        max-wait-duration: 5s # 并发调用到达上限时，阻塞等待的时长，单位：微秒。默认为 0\n  # Resilience4j 的线程池 Bulkhead 配置项，对应 ThreadPoolBulkheadProperties 属性类\n  thread-pool-bulkhead:\n    instances:\n      backendD:\n        max-thread-pool-size: 1 # 线程池的最大大小。默认为 Runtime.getRuntime().availableProcessors()\n        core-thread-pool-size: 1 # 线程池的核心大小。默认为 Runtime.getRuntime().availableProcessors() - 1\n        queue-capacity: 200 # 线程池的队列大小。默认为 100\n        keep-alive-duration: 100s # 超过核心大小的线程，空闲存活时间。默认为 20 毫秒\n\n  # Resilience4j 的重试 Retry 配置项，对应 RetryProperties 属性类\n  retry:\n    instances:\n      backendE:\n        max-retry-Attempts: 3 # 最大重试次数。默认为 3\n        wait-duration: 5s # 下次重试的间隔，单位：微秒。默认为 500 毫秒\n        retry-exceptions: # 需要重试的异常列表。默认为空\n        ingore-exceptions: # 需要忽略的异常列表。默认为空\n\n  # Resilience4j 的超时限制器 TimeLimiter 配置项，对应 TimeLimiterProperties 属性类\n  timelimiter:\n    instances:\n      backendF:\n        timeout-duration: 1s # 等待超时时间，单位：微秒。默认为 1 秒\n        cancel-running-future: true # 当等待超时时，是否关闭取消线程。默认为 true\n\nmanagement:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n  endpoint:\n    # Health 端点配置项，对应 HealthProperties 配置类\n    health:\n      show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户；可选 ALWAYS 总是展示。\n  # 健康检查配置项\n  health:\n    circuitbreakers.enabled: true\n    ratelimiters.enabled: true\n"
  },
  {
    "path": "lab-59/lab-59-resilience4j-demo01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-59</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-59-resilience4j-demo01</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Resilience4j Starter 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>io.github.resilience4j</groupId>\n            <artifactId>resilience4j-spring-boot2</artifactId>\n            <version>1.4.0</version>\n        </dependency>\n\n        <!-- 引入 Aspectj 依赖，支持 AOP 相关注解、表达式等等 -->\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjrt</artifactId>\n            <version>1.9.5</version>\n        </dependency>\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjweaver</artifactId>\n            <version>1.9.5</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-59/lab-59-resilience4j-demo01/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/DemoApplication.java",
    "content": "package cn.iocoder.springboot.lab59.resillience4jdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    @Bean\n    public RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-59/lab-59-resilience4j-demo01/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/BulkheadDemoController.java",
    "content": "package cn.iocoder.springboot.lab59.resillience4jdemo.controller;\n\nimport io.github.resilience4j.bulkhead.annotation.Bulkhead;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/bulkhead-demo\")\npublic class BulkheadDemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/get_user\")\n    @Bulkhead(name = \"backendC\", fallbackMethod = \"getUserFallback\", type = Bulkhead.Type.SEMAPHORE)\n    public String getUser(@RequestParam(\"id\") Integer id) throws InterruptedException {\n        logger.info(\"[getUser][id({})]\", id);\n        Thread.sleep(10 * 1000L); // sleep 10 秒\n        return \"User:\" + id;\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, throwable.getClass().getSimpleName());\n        return \"mock:User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "lab-59/lab-59-resilience4j-demo01/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab59.resillience4jdemo.controller;\n\nimport io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @GetMapping(\"/get_user\")\n    @CircuitBreaker(name = \"backendA\", fallbackMethod = \"getUserFallback\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        logger.info(\"[getUser][准备调用 user-service 获取用户({})详情]\", id);\n        return restTemplate.getForEntity(\"http://127.0.0.1:18080/user/get?id=\" + id, String.class).getBody();\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, throwable.getClass().getSimpleName());\n        return \"mock:User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "lab-59/lab-59-resilience4j-demo01/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/RateLimiterDemoController.java",
    "content": "package cn.iocoder.springboot.lab59.resillience4jdemo.controller;\n\nimport io.github.resilience4j.ratelimiter.annotation.RateLimiter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/rate-limiter-demo\")\npublic class RateLimiterDemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/get_user\")\n    @RateLimiter(name = \"backendB\", fallbackMethod = \"getUserFallback\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        return \"User:\" + id;\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, throwable.getClass().getSimpleName());\n        return \"mock:User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "lab-59/lab-59-resilience4j-demo01/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/RetryDemoController.java",
    "content": "package cn.iocoder.springboot.lab59.resillience4jdemo.controller;\n\nimport io.github.resilience4j.retry.annotation.Retry;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@RestController\n@RequestMapping(\"/retry-demo\")\npublic class RetryDemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @GetMapping(\"/get_user\")\n    @Retry(name = \"backendE\", fallbackMethod = \"getUserFallback\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        logger.info(\"[getUser][准备调用 user-service 获取用户({})详情]\", id);\n        return restTemplate.getForEntity(\"http://127.0.0.1:18080/user/get?id=\" + id, String.class).getBody();\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, throwable.getClass().getSimpleName());\n        return \"mock:User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "lab-59/lab-59-resilience4j-demo01/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/ThreadPoolBulkheadDemoController.java",
    "content": "package cn.iocoder.springboot.lab59.resillience4jdemo.controller;\n\nimport io.github.resilience4j.bulkhead.annotation.Bulkhead;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\n\n@RestController\n@RequestMapping(\"/thread-pool-bulkhead-demo\")\npublic class ThreadPoolBulkheadDemoController {\n\n    @Autowired\n    private ThreadPoolBulkheadService threadPoolBulkheadService;\n\n    @GetMapping(\"/get_user\")\n    public String getUser(@RequestParam(\"id\") Integer id) throws ExecutionException, InterruptedException {\n        threadPoolBulkheadService.getUser0(id);\n        return threadPoolBulkheadService.getUser0(id).get();\n    }\n\n    @Service\n    public static class ThreadPoolBulkheadService {\n\n        private Logger logger = LoggerFactory.getLogger(ThreadPoolBulkheadService.class);\n\n        @Bulkhead(name = \"backendD\", fallbackMethod = \"getUserFallback\", type = Bulkhead.Type.THREADPOOL)\n        public CompletableFuture<String> getUser0(Integer id) throws InterruptedException {\n            logger.info(\"[getUser][id({})]\", id);\n            Thread.sleep(10 * 1000L); // sleep 10 秒\n            return CompletableFuture.completedFuture(\"User:\" + id);\n        }\n\n        public CompletableFuture<String> getUserFallback(Integer id, Throwable throwable) {\n            logger.info(\"[getUserFallback][id({}) exception({})]\", id, throwable.getClass().getSimpleName());\n            return CompletableFuture.completedFuture(\"mock:User:\" + id);\n        }\n\n    }\n\n\n}\n"
  },
  {
    "path": "lab-59/lab-59-resilience4j-demo01/src/main/java/cn/iocoder/springboot/lab59/resillience4jdemo/controller/TimeLimiterDemoController.java",
    "content": "package cn.iocoder.springboot.lab59.resillience4jdemo.controller;\n\nimport io.github.resilience4j.bulkhead.annotation.Bulkhead;\nimport io.github.resilience4j.timelimiter.annotation.TimeLimiter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\n\n@RestController\n@RequestMapping(\"/time-limiter-demo\")\npublic class TimeLimiterDemoController {\n\n    @Autowired\n    private TimeLimiterService timeLimiterService;\n\n    @GetMapping(\"/get_user\")\n    public String getUser(@RequestParam(\"id\") Integer id) throws ExecutionException, InterruptedException {\n        return timeLimiterService.getUser0(id).get();\n    }\n\n    @Service\n    public static class TimeLimiterService {\n\n        private Logger logger = LoggerFactory.getLogger(TimeLimiterService.class);\n\n        @Bulkhead(name = \"backendD\", type = Bulkhead.Type.THREADPOOL)\n        @TimeLimiter(name = \"backendF\", fallbackMethod = \"getUserFallback\")\n        public CompletableFuture<String> getUser0(Integer id) throws InterruptedException {\n            logger.info(\"[getUser][id({})]\", id);\n            Thread.sleep(10 * 1000L); // sleep 10 秒\n            return CompletableFuture.completedFuture(\"User:\" + id);\n        }\n\n        public CompletableFuture<String> getUserFallback(Integer id, Throwable throwable) {\n            logger.info(\"[getUserFallback][id({}) exception({})]\", id, throwable.getClass().getSimpleName());\n            return CompletableFuture.completedFuture(\"mock:User:\" + id);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "lab-59/lab-59-resilience4j-demo01/src/main/resources/application.yml",
    "content": "resilience4j:\n  # Resilience4j 的断路器配置项，对应 CircuitBreakerProperties 属性类\n  circuitbreaker:\n    instances:\n      backendA:\n        failure-rate-threshold: 50 # 熔断器关闭状态和半开状态使用的同一个失败率阈值，单位：百分比。默认为 50\n        ring-buffer-size-in-closed-state: 5 # 熔断器关闭状态的缓冲区大小，不会限制线程的并发量，在熔断器发生状态转换前所有请求都会调用后端服务。默认为 100\n        ring-buffer-size-in-half-open-state: 5 # 熔断器半开状态的缓冲区大小，会限制线程的并发量。例如，缓冲区为 10 则每次只会允许 10 个请求调用后端服务。默认为 10\n        wait-duration-in-open-state : 5000 # 熔断器从打开状态转变为半开状态等待的时间，单位：微秒\n        automatic-transition-from-open-to-half-open-enabled: true # 如果置为 true，当等待时间结束会自动由打开变为半开；若置为 false，则需要一个请求进入来触发熔断器状态转换。默认为 true\n        register-health-indicator: true # 是否注册到健康监测\n\n  # Resilience4j 的限流器配置项，对应 RateLimiterProperties 属性类\n  ratelimiter:\n    instances:\n      backendB:\n        limit-for-period: 1 # 每个周期内，允许的请求数。默认为 50\n        limit-refresh-period: 10s # 每个周期的时长，单位：微秒。默认为 500\n        timeout-duration: 5s # 被限流时，阻塞等待的时长，单位：微秒。默认为 5s\n        register-health-indicator: true # 是否注册到健康监测\n\n  # Resilience4j 的信号量 Bulkhead 配置项，对应 BulkheadConfigurationProperties 属性类\n  bulkhead:\n    instances:\n      backendC:\n        max-concurrent-calls: 1 # 并发调用数。默认为 25\n        max-wait-duration: 5s # 并发调用到达上限时，阻塞等待的时长，单位：微秒。默认为 0\n  # Resilience4j 的线程池 Bulkhead 配置项，对应 ThreadPoolBulkheadProperties 属性类\n  thread-pool-bulkhead:\n    instances:\n      backendD:\n        max-thread-pool-size: 1 # 线程池的最大大小。默认为 Runtime.getRuntime().availableProcessors()\n        core-thread-pool-size: 1 # 线程池的核心大小。默认为 Runtime.getRuntime().availableProcessors() - 1\n        queue-capacity: 200 # 线程池的队列大小。默认为 100\n        keep-alive-duration: 100s # 超过核心大小的线程，空闲存活时间。默认为 20 毫秒\n\n  # Resilience4j 的重试 Retry 配置项，对应 RetryProperties 属性类\n  retry:\n    instances:\n      backendE:\n        max-retry-Attempts: 3 # 最大重试次数。默认为 3\n        wait-duration: 5s # 下次重试的间隔，单位：微秒。默认为 500 毫秒\n        retry-exceptions: # 需要重试的异常列表。默认为空\n        ingore-exceptions: # 需要忽略的异常列表。默认为空\n\n  # Resilience4j 的超时限制器 TimeLimiter 配置项，对应 TimeLimiterProperties 属性类\n  timelimiter:\n    instances:\n      backendF:\n        timeout-duration: 1s # 等待超时时间，单位：微秒。默认为 1 秒\n        cancel-running-future: true # 当等待超时时，是否关闭取消线程。默认为 true\n"
  },
  {
    "path": "lab-59/lab-59-user-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-59</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-59-user-service</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-59/lab-59-user-service/src/main/java/cn/iocoder/springboot/lab59/userservice/UserServiceApplication.java",
    "content": "package cn.iocoder.springboot.lab59.userservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    @RestController\n    @RequestMapping(\"/user\")\n    public class UserController {\n\n        @GetMapping(\"/get\")\n        public String get(@RequestParam(\"id\") Integer id) {\n            return \"User:\" + id;\n        }\n\n        @GetMapping(\"/batch_get\")\n        public List<String> batchGet(@RequestParam(\"ids\") List<Integer> ids) {\n            return ids.stream().map(id -> \"User:\" + id).collect(Collectors.toList());\n        }\n\n    }\n\n    public static void main(String[] args) {\n        // 设置端口\n        System.setProperty(\"server.port\", \"18080\");\n\n        // 应用启动\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-59/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-59</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>lab-59-user-service</module>\n        <module>lab-59-resilience4j-demo01</module>\n        <module>lab-59-resilience4j-actuator</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-59/《芋道 Spring Boot 服务容错 Resilience4j 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Resilience4j/?github>\n"
  },
  {
    "path": "lab-60/lab-60-soul-dubbo-demo/lab-60-soul-dubbo-demo-user-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-60-soul-dubbo-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-60-soul-dubbo-demo-user-service</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入用户服务 API 包 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-60-soul-dubbo-demo-user-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Boot 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对 Dubbo 的自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-starter</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n        <!-- 使用 Nacos 作为注册中心 -->\n        <dependency>\n            <groupId>com.alibaba.nacos</groupId>\n            <artifactId>nacos-client</artifactId>\n            <version>1.2.1</version>\n        </dependency>\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-registry-nacos</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n\n        <!-- 引入 Soul 针对 Dubbo 的集成的依赖 -->\n        <dependency>\n            <groupId>org.dromara</groupId>\n            <artifactId>soul-client-apache-dubbo</artifactId>\n            <version>2.1.2-RELEASE</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-60/lab-60-soul-dubbo-demo/lab-60-soul-dubbo-demo-user-service/src/main/java/cn/iocoder/springboot/lab60/userservice/UserServiceApplication.java",
    "content": "package cn.iocoder.springboot.lab60.userservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserServiceApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "lab-60/lab-60-soul-dubbo-demo/lab-60-soul-dubbo-demo-user-service/src/main/java/cn/iocoder/springboot/lab60/userservice/service/UserServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab60.userservice.service;\n\nimport cn.iocoder.springboot.lab60.userservice.api.UserService;\nimport cn.iocoder.springboot.lab60.userservice.api.dto.UserCreateDTO;\nimport org.dromara.soul.client.common.annotation.SoulClient;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n@org.apache.dubbo.config.annotation.Service(version = \"1.0.0\")\npublic class UserServiceImpl implements UserService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    @SoulClient(path = \"/user/get\", desc = \"获得用户详细\")\n    public String getUser(Integer id) {\n        return \"User:\" + id;\n    }\n\n    @Override\n    @SoulClient(path = \"/user/create\", desc = \"创建用户\")\n    public Integer createUser(UserCreateDTO createDTO) {\n        logger.info(\"[createUser][username({}) password({})]\", createDTO.getNickname(), createDTO.getGender());\n        return 1;\n    }\n\n}\n\n"
  },
  {
    "path": "lab-60/lab-60-soul-dubbo-demo/lab-60-soul-dubbo-demo-user-service/src/main/resources/application.yaml",
    "content": "# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 应用配置\n  application:\n    name: user-service # 应用名\n  # Dubbo 注册中心配\n  registry:\n      address: nacos://127.0.0.1:8848 # 注册中心地址。个鞥多注册中心，可见 http://dubbo.apache.org/zh-cn/docs/user/references/registry/introduction.html 文档。\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # Dubbo 服务提供者配置\n  provider:\n    timeout: 1000 # 【重要】远程服务调用超时时间，单位：毫秒。默认为 1000 毫秒，胖友可以根据自己业务修改\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springboot.lab60.userservice.service\n\nsoul:\n  # Soul 针对 Dubbo 的配置项，对应 DubboConfig 配置类\n  dubbo:\n    admin-url: http://127.0.0.1:9095 # Soul Admin 地址\n    context-path: /user-api # 设置在 Soul 网关的路由前缀，例如说 /order、/product 等等。\n                            # 后续，网关会根据该 context-path 来进行路由\n    app-name: user-service # 应用名。未配置情况下，默认使用 Dubbo 的应用名\n"
  },
  {
    "path": "lab-60/lab-60-soul-dubbo-demo/lab-60-soul-dubbo-demo-user-service-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-60-soul-dubbo-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-60-soul-dubbo-demo-user-service-api</artifactId>\n\n</project>\n"
  },
  {
    "path": "lab-60/lab-60-soul-dubbo-demo/lab-60-soul-dubbo-demo-user-service-api/src/main/java/cn/iocoder/springboot/lab60/userservice/api/UserService.java",
    "content": "package cn.iocoder.springboot.lab60.userservice.api;\n\nimport cn.iocoder.springboot.lab60.userservice.api.dto.UserCreateDTO;\n\npublic interface UserService {\n\n    String getUser(Integer id);\n\n    Integer createUser(UserCreateDTO createDTO);\n\n}\n"
  },
  {
    "path": "lab-60/lab-60-soul-dubbo-demo/lab-60-soul-dubbo-demo-user-service-api/src/main/java/cn/iocoder/springboot/lab60/userservice/api/dto/UserCreateDTO.java",
    "content": "package cn.iocoder.springboot.lab60.userservice.api.dto;\n\n/**\n * 用户创建 DTO\n */\npublic class UserCreateDTO {\n\n    /**\n     * 昵称\n     */\n    private String nickname;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public String getNickname() {\n        return nickname;\n    }\n\n    public UserCreateDTO setNickname(String nickname) {\n        this.nickname = nickname;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserCreateDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-60/lab-60-soul-dubbo-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-60</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-60-soul-dubbo-demo</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>lab-60-soul-dubbo-demo-user-service-api</module>\n        <module>lab-60-soul-dubbo-demo-user-service</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-60/lab-60-soul-spring-boot-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-60</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-60-soul-spring-boot-demo</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Soul 针对 SpringMVC 的集成的依赖 -->\n        <dependency>\n            <groupId>org.dromara</groupId>\n            <artifactId>soul-client-springmvc</artifactId>\n            <version>2.1.2-RELEASE</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-60/lab-60-soul-spring-boot-demo/src/main/java/cn/iocoder/springboot/lab60/DemoApplication.java",
    "content": "package cn.iocoder.springboot.lab60;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-60/lab-60-soul-spring-boot-demo/src/main/java/cn/iocoder/springboot/lab60/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab60.controller;\n\nimport cn.iocoder.springboot.lab60.dto.UserCreateDTO;\nimport org.dromara.soul.client.common.annotation.SoulClient;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/get\")\n    @SoulClient(path = \"/user/get\", desc = \"获得用户详细\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        return \"DEMO:\" + id;\n    }\n\n    @PostMapping(\"/create\")\n    @SoulClient(path = \"/user/create\", desc = \"创建用户\")\n    public Integer createUser(@RequestBody UserCreateDTO createDTO) {\n        logger.info(\"[createUser][username({}) password({})]\", createDTO.getNickname(), createDTO.getGender());\n        return 1;\n    }\n\n}\n"
  },
  {
    "path": "lab-60/lab-60-soul-spring-boot-demo/src/main/java/cn/iocoder/springboot/lab60/dto/UserCreateDTO.java",
    "content": "package cn.iocoder.springboot.lab60.dto;\n\n/**\n * 用户创建 DTO\n */\npublic class UserCreateDTO {\n\n    /**\n     * 昵称\n     */\n    private String nickname;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public String getNickname() {\n        return nickname;\n    }\n\n    public UserCreateDTO setNickname(String nickname) {\n        this.nickname = nickname;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserCreateDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-60/lab-60-soul-spring-boot-demo/src/main/resources/application.yaml",
    "content": "soul:\n  # Soul 针对 SpringMVC 的配置项，对应 SoulHttpConfig 配置类\n  http:\n    admin-url: http://127.0.0.1:9095 # Soul Admin 地址\n    context-path: /sb-demo-api # 设置在 Soul 网关的路由前缀，例如说 /order、/product 等等。\n                               # 后续，网关会根据该 context-path 来进行路由\n    app-name: sb-demo-service # 应用名。未配置情况下，默认使用 `spring.application.name` 配置项\n    zookeeper-url: 127.0.0.1:2181 # 使用 Zookeeper 作为注册中心的地址\n"
  },
  {
    "path": "lab-60/lab-60-soul-spring-cloud-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-60</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-60-soul-spring-cloud-demo</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Soul 针对 Spring Cloud 的集成的依赖 -->\n        <dependency>\n            <groupId>org.dromara</groupId>\n            <artifactId>soul-client-springcloud</artifactId>\n            <version>2.1.2-RELEASE</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-60/lab-60-soul-spring-cloud-demo/src/main/java/cn/iocoder/springcloud/lab60/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.lab60;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-60/lab-60-soul-spring-cloud-demo/src/main/java/cn/iocoder/springcloud/lab60/controller/UserController.java",
    "content": "package cn.iocoder.springcloud.lab60.controller;\n\nimport cn.iocoder.springcloud.lab60.dto.UserCreateDTO;\nimport org.dromara.soul.client.common.annotation.SoulClient;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/get\")\n    @SoulClient(path = \"/user/get\", desc = \"获得用户详细\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        return \"DEMO:\" + id;\n    }\n\n    @PostMapping(\"/create\")\n    @SoulClient(path = \"/user/create\", desc = \"创建用户\")\n    public Integer createUser(@RequestBody UserCreateDTO createDTO) {\n        logger.info(\"[createUser][username({}) password({})]\", createDTO.getNickname(), createDTO.getGender());\n        return 1;\n    }\n\n}\n"
  },
  {
    "path": "lab-60/lab-60-soul-spring-cloud-demo/src/main/java/cn/iocoder/springcloud/lab60/dto/UserCreateDTO.java",
    "content": "package cn.iocoder.springcloud.lab60.dto;\n\n/**\n * 用户创建 DTO\n */\npublic class UserCreateDTO {\n\n    /**\n     * 昵称\n     */\n    private String nickname;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public String getNickname() {\n        return nickname;\n    }\n\n    public UserCreateDTO setNickname(String nickname) {\n        this.nickname = nickname;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserCreateDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-60/lab-60-soul-spring-cloud-demo/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: sc-user-service # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nsoul:\n  # Soul 针对 SpringMVC 的配置项，对应 SoulSpringCloudConfig 配置类\n  springcloud:\n    admin-url: http://127.0.0.1:9095 # Soul Admin 地址\n    context-path: /sc-user-service-api # 设置在 Soul 网关的路由前缀，例如说 /order、/product 等等。\n                               # 后续，网关会根据该 context-path 来进行路由\n    app-name: sc-user-service # 应用名。未配置情况下，默认使用 `spring.application.name` 配置项\n"
  },
  {
    "path": "lab-60/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-60</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>lab-60-soul-dubbo-demo</module>\n        <module>lab-60-soul-spring-boot-demo</module>\n        <module>lab-60-soul-spring-cloud-demo</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-60/《芋道 Soul 极简入门（国产微服务网关）》.md",
    "content": "<http://www.iocoder.cn/Soul/install/?github>\n"
  },
  {
    "path": "lab-61/lab-61-cat-opentracing/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-61</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-61-cat-opentracing</artifactId>\n\n    <properties>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencies>\n        <!-- 引入 CAT 相关依赖 -->\n        <dependency>\n            <groupId>com.dianping.cat</groupId>\n            <artifactId>cat-client</artifactId>\n            <version>3.0.0</version>\n        </dependency>\n\n        <!-- 引入 Opentracing API 的依赖 -->\n        <dependency>\n            <groupId>io.opentracing</groupId>\n            <artifactId>opentracing-api</artifactId>\n            <version>0.33.0</version>\n        </dependency>\n\n    </dependencies>\n\n    <!-- 引入私服，因为 CAT 依赖并没有发到 Maven 中央仓库 -->\n    <repositories>\n        <repository>\n            <id>central</id>\n            <name>Maven2 Central Repository</name>\n            <layout>default</layout>\n            <url>http://repo1.maven.org/maven2</url>\n        </repository>\n        <repository>\n            <id>unidal.releases</id>\n            <url>http://unidal.org/nexus/content/repositories/releases/</url>\n        </repository>\n    </repositories>\n\n</project>\n"
  },
  {
    "path": "lab-61/lab-61-cat-opentracing/src/main/java/cn/iocoder/springboot/lab61/cat/opentracing/CatSpan.java",
    "content": "package cn.iocoder.springboot.lab61.cat.opentracing;\n\nimport com.dianping.cat.message.Transaction;\nimport io.opentracing.Span;\nimport io.opentracing.SpanContext;\nimport io.opentracing.tag.Tag;\n\nimport java.util.Map;\n\npublic class CatSpan implements Span {\n\n    private Transaction transaction;\n\n    public CatSpan(Transaction transaction) {\n        this.transaction = transaction;\n    }\n\n    @Override\n    public SpanContext context() {\n        return null;\n    }\n\n    @Override\n    public Span setTag(String key, String value) {\n        return null;\n    }\n\n    @Override\n    public Span setTag(String key, boolean value) {\n        return null;\n    }\n\n    @Override\n    public Span setTag(String key, Number value) {\n        return null;\n    }\n\n    @Override\n    public <T> Span setTag(Tag<T> tag, T value) {\n        return null;\n    }\n\n    @Override\n    public Span log(Map<String, ?> fields) {\n        return null;\n    }\n\n    @Override\n    public Span log(long timestampMicroseconds, Map<String, ?> fields) {\n        return null;\n    }\n\n    @Override\n    public Span log(String event) {\n        return null;\n    }\n\n    @Override\n    public Span log(long timestampMicroseconds, String event) {\n        return null;\n    }\n\n    @Override\n    public Span setBaggageItem(String key, String value) {\n        return null;\n    }\n\n    @Override\n    public String getBaggageItem(String key) {\n        return null;\n    }\n\n    @Override\n    public Span setOperationName(String operationName) {\n        return null;\n    }\n\n    @Override\n    public void finish() {\n        transaction.setStatus(Transaction.SUCCESS);\n        transaction.complete();\n    }\n\n    @Override\n    public void finish(long finishMicros) {\n\n    }\n\n}\n"
  },
  {
    "path": "lab-61/lab-61-cat-opentracing/src/main/java/cn/iocoder/springboot/lab61/cat/opentracing/CatSpanBuilder.java",
    "content": "package cn.iocoder.springboot.lab61.cat.opentracing;\n\nimport com.dianping.cat.Cat;\nimport com.dianping.cat.message.Transaction;\nimport io.opentracing.Span;\nimport io.opentracing.SpanContext;\nimport io.opentracing.Tracer;\nimport io.opentracing.tag.Tag;\n\nimport java.util.LinkedList;\nimport java.util.List;\n\npublic class CatSpanBuilder implements Tracer.SpanBuilder {\n\n    private List<cn.iocoder.springboot.lab61.cat.opentracing.Tag> tags = new LinkedList<>();\n    private String operationName;\n\n    public CatSpanBuilder(String operationName) {\n        this.operationName = operationName;\n    }\n\n    public Tracer.SpanBuilder asChildOf(SpanContext parent) {\n        return null;\n    }\n\n    public Tracer.SpanBuilder asChildOf(Span parent) {\n        return null;\n    }\n\n    public Tracer.SpanBuilder addReference(String referenceType, SpanContext referencedContext) {\n        return null;\n    }\n\n    public Tracer.SpanBuilder ignoreActiveSpan() {\n        return null;\n    }\n\n    public Tracer.SpanBuilder withTag(String key, String value) {\n        tags.add(new cn.iocoder.springboot.lab61.cat.opentracing.Tag(key, value));\n        return this;\n    }\n\n    public Tracer.SpanBuilder withTag(String key, boolean value) {\n        return null;\n    }\n\n    public Tracer.SpanBuilder withTag(String key, Number value) {\n        return null;\n    }\n\n    public <T> Tracer.SpanBuilder withTag(Tag<T> tag, T value) {\n        return null;\n    }\n\n    public Tracer.SpanBuilder withStartTimestamp(long microseconds) {\n        return null;\n    }\n\n    public Span start() {\n        Transaction transaction = Cat.newTransaction(\"Opentracing\", operationName);\n        // TODO tag 的处理\n        return new CatSpan(transaction);\n    }\n\n}\n"
  },
  {
    "path": "lab-61/lab-61-cat-opentracing/src/main/java/cn/iocoder/springboot/lab61/cat/opentracing/CatTracer.java",
    "content": "package cn.iocoder.springboot.lab61.cat.opentracing;\n\nimport io.opentracing.*;\nimport io.opentracing.propagation.Format;\n\npublic class CatTracer implements Tracer {\n\n    public ScopeManager scopeManager() {\n        return null;\n    }\n\n    public Span activeSpan() {\n        return null;\n    }\n\n    public Scope activateSpan(Span span) {\n        return null;\n    }\n\n    public SpanBuilder buildSpan(String operationName) {\n        return new CatSpanBuilder(operationName);\n    }\n\n    public <C> void inject(SpanContext spanContext, Format<C> format, C carrier) {\n\n    }\n\n    public <C> SpanContext extract(Format<C> format, C carrier) {\n        return null;\n    }\n\n    public void close() {\n\n    }\n\n}\n"
  },
  {
    "path": "lab-61/lab-61-cat-opentracing/src/main/java/cn/iocoder/springboot/lab61/cat/opentracing/Tag.java",
    "content": "package cn.iocoder.springboot.lab61.cat.opentracing;\n\npublic class Tag {\n\n    private String key;\n    private String value;\n\n    public Tag(String key, String value) {\n        this.key = key;\n        this.value = value;\n    }\n\n    public String getKey() {\n        return key;\n    }\n\n    public String getValue() {\n        return value;\n    }\n\n}\n"
  },
  {
    "path": "lab-61/lab-61-cat-opentracing-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-61</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-61-cat-opentracing-demo</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 CAT 相关依赖 -->\n        <dependency>\n            <groupId>com.dianping.cat</groupId>\n            <artifactId>cat-client</artifactId>\n            <version>3.0.0</version>\n        </dependency>\n\n        <!-- 引入 CAT + Opentracing 的集成 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-61-cat-opentracing</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n    </dependencies>\n\n    <!-- 引入私服，因为 CAT 依赖并没有发到 Maven 中央仓库 -->\n    <repositories>\n        <repository>\n            <id>central</id>\n            <name>Maven2 Central Repository</name>\n            <layout>default</layout>\n            <url>http://repo1.maven.org/maven2</url>\n        </repository>\n        <repository>\n            <id>unidal.releases</id>\n            <url>http://unidal.org/nexus/content/repositories/releases/</url>\n        </repository>\n    </repositories>\n\n</project>\n"
  },
  {
    "path": "lab-61/lab-61-cat-opentracing-demo/src/main/java/catdemo/Application.java",
    "content": "package catdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-61/lab-61-cat-opentracing-demo/src/main/java/catdemo/config/ZipkinConfig.java",
    "content": "package catdemo.config;\n\nimport cn.iocoder.springboot.lab61.cat.opentracing.CatTracer;\nimport io.opentracing.Tracer;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class ZipkinConfig {\n\n    @Bean\n    public Tracer openTracer() {\n        return new CatTracer();\n    }\n\n}\n"
  },
  {
    "path": "lab-61/lab-61-cat-opentracing-demo/src/main/java/catdemo/controller/DemoController.java",
    "content": "package catdemo.controller;\n\nimport com.dianping.cat.Cat;\nimport io.opentracing.Span;\nimport io.opentracing.Tracer;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private Tracer tracer;\n\n    /**\n     * 监控模型 Transaction 的示例\n     */\n    @GetMapping(\"/test\")\n    public String test() {\n        // 创建一个 Span\n        Span parentSpan = tracer.buildSpan(\"parent\").start();\n\n        Span childSpan = tracer.buildSpan(\"child\").start();\n        childSpan.finish();\n\n        Cat.logEvent(\"测试事件\", \"123\");\n\n        // 结束一个 Span\n        parentSpan.finish();\n\n        // 返回\n        return \"opentracing\";\n    }\n\n}\n"
  },
  {
    "path": "lab-61/lab-61-cat-opentracing-demo/src/main/resources/META-INF/app.properties",
    "content": "app.name=demo-application\n"
  },
  {
    "path": "lab-61/lab-61-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-61</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-61-demo</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 CAT 相关依赖 -->\n        <dependency>\n            <groupId>com.dianping.cat</groupId>\n            <artifactId>cat-client</artifactId>\n            <version>3.0.0</version>\n        </dependency>\n    </dependencies>\n\n    <!-- 引入私服，因为 CAT 依赖并没有发到 Maven 中央仓库 -->\n    <repositories>\n        <repository>\n            <id>central</id>\n            <name>Maven2 Central Repository</name>\n            <layout>default</layout>\n            <url>http://repo1.maven.org/maven2</url>\n        </repository>\n        <repository>\n            <id>unidal.releases</id>\n            <url>http://unidal.org/nexus/content/repositories/releases/</url>\n        </repository>\n    </repositories>\n\n</project>\n"
  },
  {
    "path": "lab-61/lab-61-demo/src/main/java/cn/iocoder/springboot/lab61/catdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab61.catdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-61/lab-61-demo/src/main/java/cn/iocoder/springboot/lab61/catdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab61.catdemo.controller;\n\nimport com.dianping.cat.Cat;\nimport com.dianping.cat.message.Event;\nimport com.dianping.cat.message.Transaction;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    /**\n     * 监控模型 Transaction 的示例\n     */\n    @GetMapping(\"/transaction\")\n    public String transaction() {\n        // 创建 Transaction 对象\n        Transaction t = Cat.newTransaction(\"URL\", \"/demo/transaction\");\n        try {\n            // ... yourBusiness(); 业务逻辑\n\n            // 设置 Transaction 的状态为成功\n            t.setStatus(Transaction.SUCCESS);\n        } catch (Exception e) {\n            // 设置 Transaction 的状态为异常\n            t.setStatus(e);\n        } finally {\n            // 标记 Transaction 结束\n            t.complete();\n        }\n        return \"success\";\n    }\n\n    /**\n     * 监控模型 Event 的示例 01\n     */\n    @GetMapping(\"/event-01\")\n    public String event01() {\n        // Cat.logEvent(\"URL.Server\", \"127.0.0.1\");\n        Cat.logEvent(\"URL.Server\", \"127.0.0.1\", Event.SUCCESS, \"data\");\n        return \"success\";\n    }\n\n    /**\n     * 监控模型 Event 的示例 02\n     */\n    @GetMapping(\"/event-02\")\n    public String event02() {\n        try {\n            int result = 1 / 0;\n        } catch (Throwable e) {\n            Cat.logError(e);\n            // Cat.logError(\"custom-message\", e);\n        }\n        return \"success\";\n    }\n\n    /**\n     * 监控模型 Event 的示例 03\n     */\n    @GetMapping(\"/event-03\")\n    public String event03() {\n        try {\n            int result = 1 / 0;\n        } catch (Throwable e) {\n            Cat.logErrorWithCategory(\"custom-category\", e);\n            // Cat.logErrorWithCategory(\"custom-category\", \"custom-message\", e);\n        }\n        return \"success\";\n    }\n\n    /**\n     * 监控模型 Metric 示例 01\n     */\n    @GetMapping(\"/metric-01\")\n    public String metric01() {\n        Cat.logMetricForCount(\"visit.count\", 1);\n        return \"success\";\n    }\n\n    /**\n     * 监控模型 Metric 示例 02\n     */\n    @GetMapping(\"/metric-02\")\n    public String metric02() {\n        Cat.logMetricForDuration(\"visit.duration\", 10L);\n        return \"success\";\n    }\n\n}\n"
  },
  {
    "path": "lab-61/lab-61-demo/src/main/resources/META-INF/app.properties",
    "content": "app.name=demo-application\n"
  },
  {
    "path": "lab-61/lab-61-logback/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-61</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-61-logback</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 CAT 相关依赖 -->\n        <dependency>\n            <groupId>com.dianping.cat</groupId>\n            <artifactId>cat-client</artifactId>\n            <version>3.0.0</version>\n        </dependency>\n    </dependencies>\n\n    <!-- 引入私服，因为 CAT 依赖并没有发到 Maven 中央仓库 -->\n    <repositories>\n        <repository>\n            <id>central</id>\n            <name>Maven2 Central Repository</name>\n            <layout>default</layout>\n            <url>http://repo1.maven.org/maven2</url>\n        </repository>\n        <repository>\n            <id>unidal.releases</id>\n            <url>http://unidal.org/nexus/content/repositories/releases/</url>\n        </repository>\n    </repositories>\n\n</project>\n"
  },
  {
    "path": "lab-61/lab-61-logback/src/main/java/cn/iocoder/springboot/lab61/catdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab61.catdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-61/lab-61-logback/src/main/java/cn/iocoder/springboot/lab61/catdemo/controller/LoggerController.java",
    "content": "package cn.iocoder.springboot.lab61.catdemo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/logger\")\npublic class LoggerController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/error\")\n    public String error() {\n        try {\n            int result = 1 / 0;\n        } catch (Throwable e) {\n            logger.error(\"计算异常\", e);\n        }\n        return \"success\";\n    }\n\n}\n"
  },
  {
    "path": "lab-61/lab-61-logback/src/main/resources/META-INF/app.properties",
    "content": "app.name=demo-application\n"
  },
  {
    "path": "lab-61/lab-61-logback/src/main/resources/logback-spring.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<configuration>\n\n    <!-- 参考 -->\n    <include resource=\"org/springframework/boot/logging/logback/defaults.xml\" />\n    <property name=\"LOG_FILE\" value=\"${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/spring.log}\"/>\n    <include resource=\"org/springframework/boot/logging/logback/console-appender.xml\" />\n    <include resource=\"org/springframework/boot/logging/logback/file-appender.xml\" />\n\n    <!-- 定义 Sentry Appender -->\n    <appender name=\"CatAppender\" class=\"com.dianping.cat.logback.CatLogbackAppender\" />\n\n    <!-- 日志输出级别 -->\n    <root level=\"INFO\">\n        <appender-ref ref=\"CONSOLE\" />\n        <appender-ref ref=\"FILE\" />\n        <appender-ref ref=\"CatAppender\" />\n    </root>\n\n</configuration>\n"
  },
  {
    "path": "lab-61/lab-61-springmvc/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-61</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-61-springmvc</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 CAT 相关依赖 -->\n        <dependency>\n            <groupId>com.dianping.cat</groupId>\n            <artifactId>cat-client</artifactId>\n            <version>3.0.0</version>\n        </dependency>\n    </dependencies>\n\n    <!-- 引入私服，因为 CAT 依赖并没有发到 Maven 中央仓库 -->\n    <repositories>\n        <repository>\n            <id>central</id>\n            <name>Maven2 Central Repository</name>\n            <layout>default</layout>\n            <url>http://repo1.maven.org/maven2</url>\n        </repository>\n        <repository>\n            <id>unidal.releases</id>\n            <url>http://unidal.org/nexus/content/repositories/releases/</url>\n        </repository>\n    </repositories>\n\n</project>\n"
  },
  {
    "path": "lab-61/lab-61-springmvc/src/main/java/cn/iocoder/springboot/lab61/catdemo/Application.java",
    "content": "package cn.iocoder.springboot.lab61.catdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-61/lab-61-springmvc/src/main/java/cn/iocoder/springboot/lab61/catdemo/config/CatFilterConfigure.java",
    "content": "package cn.iocoder.springboot.lab61.catdemo.config;\n\nimport com.dianping.cat.servlet.CatFilter;\nimport org.springframework.boot.web.servlet.FilterRegistrationBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class CatFilterConfigure {\n\n    @Bean\n    public FilterRegistrationBean<CatFilter> catFilter() {\n        // 创建 CatFilter 对象\n        CatFilter filter = new CatFilter();\n        // 创建 FilterRegistrationBean 对象\n        FilterRegistrationBean<CatFilter> registration = new FilterRegistrationBean<>();\n        registration.setFilter(filter);\n        registration.addUrlPatterns(\"/*\"); // 匹配所有 URL\n        registration.setName(\"cat-filter\");\n        registration.setOrder(1);\n        return registration;\n    }\n\n}\n"
  },
  {
    "path": "lab-61/lab-61-springmvc/src/main/java/cn/iocoder/springboot/lab61/catdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab61.catdemo.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @GetMapping(\"/hello\")\n    public String hello() {\n        return \"world\";\n    }\n\n}\n"
  },
  {
    "path": "lab-61/lab-61-springmvc/src/main/resources/META-INF/app.properties",
    "content": "app.name=demo-application\n"
  },
  {
    "path": "lab-61/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-61</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-61-demo</module>\n        <module>lab-61-logback</module>\n        <module>lab-61-springmvc</module>\n        <module>lab-61-cat-opentracing</module>\n        <module>lab-61-cat-opentracing-demo</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-61/《芋道 Spring Boot 监控平台 CAT 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/CAT/?github>\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-annotations-demo/lab-62-sofarpc-annotations-demo-user-rpc-service-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-62-sofarpc-xml-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-62-sofarpc-annotations-demo-user-rpc-service-api</artifactId>\n\n\n\n</project>\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-annotations-demo/lab-62-sofarpc-annotations-demo-user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab62/rpc/api/UserRpcService.java",
    "content": "package cn.iocoder.springboot.lab62.rpc.api;\n\nimport cn.iocoder.springboot.lab62.rpc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab62.rpc.dto.UserDTO;\n\n/**\n * 用户服务 RPC Service 接口\n */\npublic interface UserRpcService {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    UserDTO get(Integer id);\n\n    /**\n     * 添加新用户，返回新添加的用户编号\n     *\n     * @param addDTO 添加的用户信息\n     * @return 用户编号\n     */\n    Integer add(UserAddDTO addDTO);\n\n}\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-annotations-demo/lab-62-sofarpc-annotations-demo-user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab62/rpc/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springboot.lab62.rpc.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO implements Serializable {\n\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public String getName() {\n        return name;\n    }\n\n    public UserAddDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserAddDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-annotations-demo/lab-62-sofarpc-annotations-demo-user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab62/rpc/dto/UserDTO.java",
    "content": "package cn.iocoder.springboot.lab62.rpc.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户信息 DTO\n */\npublic class UserDTO implements Serializable {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-annotations-demo/lab-62-sofarpc-annotations-demo-user-rpc-service-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <!--        <artifactId>lab-62-sofarpc-xml-demo</artifactId>-->\n        <!--        <groupId>cn.iocoder.springboot.labs</groupId>-->\n        <!--        <version>1.0-SNAPSHOT</version>-->\n        <groupId>com.alipay.sofa</groupId>\n        <artifactId>sofaboot-dependencies</artifactId>\n        <version>3.3.2</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-62-sofarpc-annotations-demo-user-rpc-service-consumer</artifactId>\n\n    <dependencies>\n        <!-- 引入定义的 SOFARPC API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-62-sofarpc-annotations-demo-user-rpc-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 SpringMVC 的 Spring Boot 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 SOFARPC 的自动化配置 -->\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>rpc-sofa-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 使用 Zookeeper 作为注册中心 -->\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-framework</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-annotations-demo/lab-62-sofarpc-annotations-demo-user-rpc-service-consumer/src/main/java/cn/iocoder/springboot/lab62/rpc/ConsumerApplication.java",
    "content": "package cn.iocoder.springboot.lab62.rpc;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n//@ImportResource(\"classpath:sofarpc.xml\")\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(ConsumerApplication.class, args);;\n    }\n\n}\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-annotations-demo/lab-62-sofarpc-annotations-demo-user-rpc-service-consumer/src/main/java/cn/iocoder/springboot/lab62/rpc/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab62.rpc.controller;\n\nimport cn.iocoder.springboot.lab62.rpc.api.UserRpcService;\nimport cn.iocoder.springboot.lab62.rpc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab62.rpc.dto.UserDTO;\nimport com.alipay.sofa.runtime.api.annotation.SofaReference;\nimport com.alipay.sofa.runtime.api.annotation.SofaReferenceBinding;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @SofaReference(binding = @SofaReferenceBinding(bindingType = \"bolt\"))\n    private UserRpcService userRpcService;\n\n    @GetMapping(\"/get\")\n    public UserDTO get(@RequestParam(\"id\") Integer id) {\n        return userRpcService.get(id);\n    }\n\n    @GetMapping(\"/add\") // 为了方便测试，实际使用 @PostMapping\n    public Integer add(@RequestParam(\"name\") String name,\n                       @RequestParam(\"gender\") Integer gender) {\n        UserAddDTO addDTO = new UserAddDTO().setName(name).setGender(gender);\n        return userRpcService.add(addDTO);\n    }\n\n}\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-annotations-demo/lab-62-sofarpc-annotations-demo-user-rpc-service-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: user-service-consumer # 应用名\n\ncom.alipay.sofa.rpc.registry.address: zookeeper://127.0.0.1:2181 # SOFARPC 注解中心\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-annotations-demo/lab-62-sofarpc-annotations-demo-user-rpc-service-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n<!--        <artifactId>lab-62-sofarpc-xml-demo</artifactId>-->\n<!--        <groupId>cn.iocoder.springboot.labs</groupId>-->\n<!--        <version>1.0-SNAPSHOT</version>-->\n        <groupId>com.alipay.sofa</groupId>\n        <artifactId>sofaboot-dependencies</artifactId>\n        <version>3.4.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-62-sofarpc-annotations-demo-user-rpc-service-provider</artifactId>\n\n<!--    <dependencyManagement>-->\n<!--        <dependencies>-->\n<!--            <dependency>-->\n<!--                <groupId>com.alipay.sofa</groupId>-->\n<!--                <artifactId>sofaboot-dependencies</artifactId>-->\n<!--                <version>3.3.2</version>-->\n<!--                <type>pom</type>-->\n<!--            </dependency>-->\n<!--        </dependencies>-->\n<!--    </dependencyManagement>-->\n\n    <dependencies>\n        <!-- 引入定义的 SOFARPC API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-62-sofarpc-annotations-demo-user-rpc-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Boot 基础 Starter 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对 SOFARPC 的自动化配置 -->\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>rpc-sofa-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 使用 Zookeeper 作为注册中心 -->\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-framework</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-annotations-demo/lab-62-sofarpc-annotations-demo-user-rpc-service-provider/src/main/java/cn/iocoder/springboot/lab62/rpc/ProviderApplication.java",
    "content": "package cn.iocoder.springboot.lab62.rpc;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n//@ImportResource(\"classpath:sofarpc.xml\")\npublic class ProviderApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(ProviderApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-annotations-demo/lab-62-sofarpc-annotations-demo-user-rpc-service-provider/src/main/java/cn/iocoder/springboot/lab62/rpc/service/UserRpcServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab62.rpc.service;\n\n\nimport cn.iocoder.springboot.lab62.rpc.api.UserRpcService;\nimport cn.iocoder.springboot.lab62.rpc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab62.rpc.dto.UserDTO;\nimport com.alipay.sofa.runtime.api.annotation.SofaService;\nimport com.alipay.sofa.runtime.api.annotation.SofaServiceBinding;\nimport org.springframework.stereotype.Service;\n\n@Service\n@SofaService(bindings = @SofaServiceBinding(bindingType = \"bolt\"))\npublic class UserRpcServiceImpl implements UserRpcService {\n\n    @Override\n    public UserDTO get(Integer id) {\n        return new UserDTO().setId(id)\n                .setName(\"没有昵称：\" + id)\n                .setGender(id % 2 + 1); // 1 - 男；2 - 女\n    }\n\n    @Override\n    public Integer add(UserAddDTO addDTO) {\n        return (int) (System.currentTimeMillis() / 1000); // 嘿嘿，随便返回一个 id\n    }\n\n}\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-annotations-demo/lab-62-sofarpc-annotations-demo-user-rpc-service-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: user-service-provider # 应用名\n\ncom.alipay.sofa.rpc.registry.address: zookeeper://127.0.0.1:2181 # SOFA RPC 注册中心\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-annotations-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-62</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-62-sofarpc-annotations-demo</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>lab-62-sofarpc-annotations-demo-user-rpc-service-api</module>\n        <module>lab-62-sofarpc-annotations-demo-user-rpc-service-provider</module>\n        <module>lab-62-sofarpc-annotations-demo-user-rpc-service-consumer</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-62-sofarpc-xml-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-62-sofarpc-xml-demo-user-rpc-service-api</artifactId>\n\n</project>\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab62/rpc/api/UserRpcService.java",
    "content": "package cn.iocoder.springboot.lab62.rpc.api;\n\nimport cn.iocoder.springboot.lab62.rpc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab62.rpc.dto.UserDTO;\n\n/**\n * 用户服务 RPC Service 接口\n */\npublic interface UserRpcService {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    UserDTO get(Integer id);\n\n    /**\n     * 添加新用户，返回新添加的用户编号\n     *\n     * @param addDTO 添加的用户信息\n     * @return 用户编号\n     */\n    Integer add(UserAddDTO addDTO);\n\n}\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab62/rpc/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springboot.lab62.rpc.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO implements Serializable {\n\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public String getName() {\n        return name;\n    }\n\n    public UserAddDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserAddDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab62/rpc/dto/UserDTO.java",
    "content": "package cn.iocoder.springboot.lab62.rpc.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户信息 DTO\n */\npublic class UserDTO implements Serializable {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <!--        <artifactId>lab-62-sofarpc-xml-demo</artifactId>-->\n        <!--        <groupId>cn.iocoder.springboot.labs</groupId>-->\n        <!--        <version>1.0-SNAPSHOT</version>-->\n        <groupId>com.alipay.sofa</groupId>\n        <artifactId>sofaboot-dependencies</artifactId>\n        <version>3.3.2</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-62-sofarpc-xml-demo-user-rpc-service-consumer</artifactId>\n\n    <dependencies>\n        <!-- 引入定义的 SOFARPC API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-62-sofarpc-xml-demo-user-rpc-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 SpringMVC 的 Spring Boot 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 SOFARPC 的自动化配置 -->\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>rpc-sofa-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 使用 Zookeeper 作为注册中心 -->\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-framework</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-consumer/src/main/java/cn/iocoder/springboot/lab62/rpc/ConsumerApplication.java",
    "content": "package cn.iocoder.springboot.lab62.rpc;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.ImportResource;\n\n@SpringBootApplication\n@ImportResource(\"classpath:sofarpc.xml\")\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(ConsumerApplication.class, args);;\n    }\n\n}\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-consumer/src/main/java/cn/iocoder/springboot/lab62/rpc/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab62.rpc.controller;\n\nimport cn.iocoder.springboot.lab62.rpc.api.UserRpcService;\nimport cn.iocoder.springboot.lab62.rpc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab62.rpc.dto.UserDTO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Autowired\n    private UserRpcService userRpcService;\n\n    @GetMapping(\"/get\")\n    public UserDTO get(@RequestParam(\"id\") Integer id) {\n        return userRpcService.get(id);\n    }\n\n    @GetMapping(\"/add\") // 为了方便测试，实际使用 @PostMapping\n    public Integer add(@RequestParam(\"name\") String name,\n                       @RequestParam(\"gender\") Integer gender) {\n        UserAddDTO addDTO = new UserAddDTO().setName(name).setGender(gender);\n        return userRpcService.add(addDTO);\n    }\n\n}\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: user-service-consumer # 应用名\n\ncom.alipay.sofa.rpc.registry.address: zookeeper://127.0.0.1:2181 # SOFARPC 注解中心\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-consumer/src/main/resources/sofarpc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:sofa=\"http://sofastack.io/schema/sofaboot\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\n            http://sofastack.io/schema/sofaboot http://sofastack.io/schema/sofaboot.xsd\">\n\n    <sofa:reference id=\"userRpcService\" interface=\"cn.iocoder.springboot.lab62.rpc.api.UserRpcService\">\n        <sofa:binding.bolt/>\n    </sofa:reference>\n\n</beans>\n\n\n\n\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n<!--        <artifactId>lab-62-sofarpc-xml-demo</artifactId>-->\n<!--        <groupId>cn.iocoder.springboot.labs</groupId>-->\n<!--        <version>1.0-SNAPSHOT</version>-->\n        <groupId>com.alipay.sofa</groupId>\n        <artifactId>sofaboot-dependencies</artifactId>\n        <version>3.4.0</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-62-sofarpc-xml-demo-user-rpc-service-provider</artifactId>\n\n<!--    <dependencyManagement>-->\n<!--        <dependencies>-->\n<!--            <dependency>-->\n<!--                <groupId>com.alipay.sofa</groupId>-->\n<!--                <artifactId>sofaboot-dependencies</artifactId>-->\n<!--                <version>3.3.2</version>-->\n<!--                <type>pom</type>-->\n<!--            </dependency>-->\n<!--        </dependencies>-->\n<!--    </dependencyManagement>-->\n\n    <dependencies>\n        <!-- 引入定义的 SOFARPC API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-62-sofarpc-xml-demo-user-rpc-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Boot 基础 Starter 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对 SOFARPC 的自动化配置 -->\n        <dependency>\n            <groupId>com.alipay.sofa</groupId>\n            <artifactId>rpc-sofa-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 使用 Zookeeper 作为注册中心 -->\n        <dependency>\n            <groupId>org.apache.curator</groupId>\n            <artifactId>curator-framework</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-provider/src/main/java/cn/iocoder/springboot/lab62/rpc/ProviderApplication.java",
    "content": "package cn.iocoder.springboot.lab62.rpc;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.ImportResource;\n\n@SpringBootApplication\n@ImportResource(\"classpath:sofarpc.xml\")\npublic class ProviderApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(ProviderApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-provider/src/main/java/cn/iocoder/springboot/lab62/rpc/service/UserRpcServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab62.rpc.service;\n\n\nimport cn.iocoder.springboot.lab62.rpc.api.UserRpcService;\nimport cn.iocoder.springboot.lab62.rpc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab62.rpc.dto.UserDTO;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class UserRpcServiceImpl implements UserRpcService {\n\n    @Override\n    public UserDTO get(Integer id) {\n        return new UserDTO().setId(id)\n                .setName(\"没有昵称：\" + id)\n                .setGender(id % 2 + 1); // 1 - 男；2 - 女\n    }\n\n    @Override\n    public Integer add(UserAddDTO addDTO) {\n        return (int) (System.currentTimeMillis() / 1000); // 嘿嘿，随便返回一个 id\n    }\n\n}\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: user-service-provider # 应用名\n\ncom.alipay.sofa.rpc.registry.address: zookeeper://127.0.0.1:2181 # SOFA RPC 注册中心\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-xml-demo/lab-62-sofarpc-xml-demo-user-rpc-service-provider/src/main/resources/sofarpc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:sofa=\"http://sofastack.io/schema/sofaboot\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\n            http://sofastack.io/schema/sofaboot http://sofastack.io/schema/sofaboot.xsd\">\n\n    <sofa:service ref=\"userRpcServiceImpl\" interface=\"cn.iocoder.springboot.lab62.rpc.api.UserRpcService\">\n        <sofa:binding.bolt/>\n    </sofa:service>\n\n</beans>\n"
  },
  {
    "path": "lab-62/lab-62-sofarpc-xml-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-62</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-62-sofarpc-xml-demo</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-62-sofarpc-xml-demo-user-rpc-service-api</module>\n        <module>lab-62-sofarpc-xml-demo-user-rpc-service-provider</module>\n        <module>lab-62-sofarpc-xml-demo-user-rpc-service-consumer</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-62/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-62</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>lab-62-sofarpc-xml-demo</module>\n        <module>lab-62-sofarpc-annotations-demo</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-62/《芋道 Spring Boot SOFARPC 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/SOFARPC/?github>\n"
  },
  {
    "path": "lab-63/lab-63-motan-annotations-demo/lab-63-motan-annotations-demo-user-rpc-service-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-63-motan-annotations-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-63-motan-annotations-demo-user-rpc-service-api</artifactId>\n\n</project>\n"
  },
  {
    "path": "lab-63/lab-63-motan-annotations-demo/lab-63-motan-annotations-demo-user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab63/rpc/api/UserRpcService.java",
    "content": "package cn.iocoder.springboot.lab63.rpc.api;\n\nimport cn.iocoder.springboot.lab63.rpc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab63.rpc.dto.UserDTO;\n\n/**\n * 用户服务 RPC Service 接口\n */\npublic interface UserRpcService {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    UserDTO get(Integer id);\n\n    /**\n     * 添加新用户，返回新添加的用户编号\n     *\n     * @param addDTO 添加的用户信息\n     * @return 用户编号\n     */\n    Integer add(UserAddDTO addDTO);\n\n}\n"
  },
  {
    "path": "lab-63/lab-63-motan-annotations-demo/lab-63-motan-annotations-demo-user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab63/rpc/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springboot.lab63.rpc.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO implements Serializable {\n\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public String getName() {\n        return name;\n    }\n\n    public UserAddDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserAddDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-63/lab-63-motan-annotations-demo/lab-63-motan-annotations-demo-user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab63/rpc/dto/UserDTO.java",
    "content": "package cn.iocoder.springboot.lab63.rpc.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户信息 DTO\n */\npublic class UserDTO implements Serializable {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-63/lab-63-motan-annotations-demo/lab-63-motan-annotations-demo-user-rpc-service-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-63-motan-annotations-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-63-motan-annotations-demo-user-rpc-service-consumer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <moton.version>1.1.8</moton.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Motan API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-63-motan-annotations-demo-user-rpc-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 SpringMVC 的 Spring Boot 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Motan 相关依赖 -->\n        <dependency>\n            <groupId>com.weibo</groupId>\n            <artifactId>motan-core</artifactId>\n            <version>${moton.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.weibo</groupId>\n            <artifactId>motan-transport-netty4</artifactId>\n            <version>${moton.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.weibo</groupId>\n            <artifactId>motan-registry-zookeeper</artifactId>\n            <version>${moton.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.weibo</groupId>\n            <artifactId>motan-springsupport</artifactId>\n            <version>${moton.version}</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-63/lab-63-motan-annotations-demo/lab-63-motan-annotations-demo-user-rpc-service-consumer/src/main/java/cn/iocoder/springboot/lab63/rpc/ConsumerApplication.java",
    "content": "package cn.iocoder.springboot.lab63.rpc;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.ImportResource;\n\n@SpringBootApplication\n@ImportResource(\"classpath:motan.xml\")\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(ConsumerApplication.class, args);;\n    }\n\n}\n"
  },
  {
    "path": "lab-63/lab-63-motan-annotations-demo/lab-63-motan-annotations-demo-user-rpc-service-consumer/src/main/java/cn/iocoder/springboot/lab63/rpc/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab63.rpc.controller;\n\nimport cn.iocoder.springboot.lab63.rpc.api.UserRpcService;\nimport cn.iocoder.springboot.lab63.rpc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab63.rpc.dto.UserDTO;\nimport com.weibo.api.motan.config.springsupport.annotation.MotanReferer;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n//    @Autowired\n    @MotanReferer\n    private UserRpcService userRpcService;\n\n    @GetMapping(\"/get\")\n    public UserDTO get(@RequestParam(\"id\") Integer id) {\n        return userRpcService.get(id);\n    }\n\n    @GetMapping(\"/add\") // 为了方便测试，实际使用 @PostMapping\n    public Integer add(@RequestParam(\"name\") String name,\n                       @RequestParam(\"gender\") Integer gender) {\n        UserAddDTO addDTO = new UserAddDTO().setName(name).setGender(gender);\n        return userRpcService.add(addDTO);\n    }\n\n}\n"
  },
  {
    "path": "lab-63/lab-63-motan-annotations-demo/lab-63-motan-annotations-demo-user-rpc-service-consumer/src/main/resources/motan.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:motan=\"http://api.weibo.com/schema/motan\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\n       http://api.weibo.com/schema/motan http://api.weibo.com/schema/motan.xsd\">\n\n    <!-- 注册中心配置。 -->\n    <motan:registry name=\"registry\" regProtocol=\"zookeeper\" address=\"127.0.0.1:2181\" connectTimeout=\"2000\"/>\n\n    <!-- Motan 协议配置。-->\n    <motan:protocol name=\"motan2\" default=\"true\"\n                    haStrategy=\"failover\" loadbalance=\"roundrobin\"\n                    maxClientConnection=\"10\" minClientConnection=\"2\"/>\n\n    <!-- Motan 服务调用方配置。 -->\n<!--    <motan:referer id=\"userRpcService\" interface=\"cn.iocoder.springboot.lab63.rpc.api.UserRpcService\" />-->\n\n    <motan:annotation />\n\n</beans>\n"
  },
  {
    "path": "lab-63/lab-63-motan-annotations-demo/lab-63-motan-annotations-demo-user-rpc-service-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-63-motan-annotations-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-63-motan-annotations-demo-user-rpc-service-provider</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <moton.version>1.1.8</moton.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Motan API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-63-motan-annotations-demo-user-rpc-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Boot 基础 Starter 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 引入 Motan 相关依赖 -->\n        <dependency>\n            <groupId>com.weibo</groupId>\n            <artifactId>motan-core</artifactId>\n            <version>${moton.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.weibo</groupId>\n            <artifactId>motan-transport-netty4</artifactId>\n            <version>${moton.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.weibo</groupId>\n            <artifactId>motan-registry-zookeeper</artifactId>\n            <version>${moton.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.weibo</groupId>\n            <artifactId>motan-springsupport</artifactId>\n            <version>${moton.version}</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-63/lab-63-motan-annotations-demo/lab-63-motan-annotations-demo-user-rpc-service-provider/src/main/java/cn/iocoder/springboot/lab63/rpc/ProviderApplication.java",
    "content": "package cn.iocoder.springboot.lab63.rpc;\n\nimport com.weibo.api.motan.common.MotanConstants;\nimport com.weibo.api.motan.util.MotanSwitcherUtil;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.ImportResource;\n\n@SpringBootApplication\n@ImportResource(\"classpath:motan.xml\")\npublic class ProviderApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(ProviderApplication.class, args);\n        // 设置 Motan 开启对外服务\n        MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, true);\n    }\n\n}\n"
  },
  {
    "path": "lab-63/lab-63-motan-annotations-demo/lab-63-motan-annotations-demo-user-rpc-service-provider/src/main/java/cn/iocoder/springboot/lab63/rpc/service/UserRpcServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab63.rpc.service;\n\n\nimport cn.iocoder.springboot.lab63.rpc.api.UserRpcService;\nimport cn.iocoder.springboot.lab63.rpc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab63.rpc.dto.UserDTO;\nimport com.weibo.api.motan.config.springsupport.annotation.MotanService;\nimport org.springframework.stereotype.Service;\n\n@Service\n@MotanService(export = \"motan2:8001\")\npublic class UserRpcServiceImpl implements UserRpcService {\n\n    @Override\n    public UserDTO get(Integer id) {\n        return new UserDTO().setId(id)\n                .setName(\"没有昵称：\" + id)\n                .setGender(id % 2 + 1); // 1 - 男；2 - 女\n    }\n\n    @Override\n    public Integer add(UserAddDTO addDTO) {\n        return (int) (System.currentTimeMillis() / 1000); // 嘿嘿，随便返回一个 id\n    }\n\n}\n"
  },
  {
    "path": "lab-63/lab-63-motan-annotations-demo/lab-63-motan-annotations-demo-user-rpc-service-provider/src/main/resources/motan.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:motan=\"http://api.weibo.com/schema/motan\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\n       http://api.weibo.com/schema/motan http://api.weibo.com/schema/motan.xsd\">\n\n    <!-- 注册中心配置。 -->\n    <motan:registry name=\"registry\" regProtocol=\"zookeeper\" address=\"127.0.0.1:2181\" connectTimeout=\"2000\" />\n\n    <!-- Motan 协议配置。-->\n    <motan:protocol name=\"motan2\" default=\"true\"\n                    maxServerConnection=\"80000\" maxContentLength=\"1048576\"\n                    maxWorkerThread=\"800\" minWorkerThread=\"20\" />\n\n    <!-- Motan 服务提供方配置。 -->\n<!--    <motan:service ref=\"userRpcServiceImpl\" interface=\"cn.iocoder.springboot.lab63.rpc.api.UserRpcService\" export=\"motan2:8001\" />-->\n\n    <motan:annotation package=\"cn.iocoder.springboot.lab63.rpc.service\" />\n\n</beans>\n"
  },
  {
    "path": "lab-63/lab-63-motan-annotations-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-63</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-63-motan-annotations-demo</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-63-motan-annotations-demo-user-rpc-service-api</module>\n        <module>lab-63-motan-annotations-demo-user-rpc-service-provider</module>\n        <module>lab-63-motan-annotations-demo-user-rpc-service-consumer</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-63/lab-63-motan-xml-demo/lab-63-motan-xml-demo-user-rpc-service-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-63-motan-xml-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-63-motan-xml-demo-user-rpc-service-api</artifactId>\n\n</project>\n"
  },
  {
    "path": "lab-63/lab-63-motan-xml-demo/lab-63-motan-xml-demo-user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab63/rpc/api/UserRpcService.java",
    "content": "package cn.iocoder.springboot.lab63.rpc.api;\n\nimport cn.iocoder.springboot.lab63.rpc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab63.rpc.dto.UserDTO;\n\n/**\n * 用户服务 RPC Service 接口\n */\npublic interface UserRpcService {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    UserDTO get(Integer id);\n\n    /**\n     * 添加新用户，返回新添加的用户编号\n     *\n     * @param addDTO 添加的用户信息\n     * @return 用户编号\n     */\n    Integer add(UserAddDTO addDTO);\n\n}\n"
  },
  {
    "path": "lab-63/lab-63-motan-xml-demo/lab-63-motan-xml-demo-user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab63/rpc/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springboot.lab63.rpc.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO implements Serializable {\n\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public String getName() {\n        return name;\n    }\n\n    public UserAddDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserAddDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-63/lab-63-motan-xml-demo/lab-63-motan-xml-demo-user-rpc-service-api/src/main/java/cn/iocoder/springboot/lab63/rpc/dto/UserDTO.java",
    "content": "package cn.iocoder.springboot.lab63.rpc.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户信息 DTO\n */\npublic class UserDTO implements Serializable {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-63/lab-63-motan-xml-demo/lab-63-motan-xml-demo-user-rpc-service-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-63-motan-xml-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-63-motan-xml-demo-user-rpc-service-consumer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <moton.version>1.1.8</moton.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Motan API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-63-motan-xml-demo-user-rpc-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 SpringMVC 的 Spring Boot 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Motan 相关依赖 -->\n        <dependency>\n            <groupId>com.weibo</groupId>\n            <artifactId>motan-core</artifactId>\n            <version>${moton.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.weibo</groupId>\n            <artifactId>motan-transport-netty4</artifactId>\n            <version>${moton.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.weibo</groupId>\n            <artifactId>motan-registry-zookeeper</artifactId>\n            <version>${moton.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.weibo</groupId>\n            <artifactId>motan-springsupport</artifactId>\n            <version>${moton.version}</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-63/lab-63-motan-xml-demo/lab-63-motan-xml-demo-user-rpc-service-consumer/src/main/java/cn/iocoder/springboot/lab63/rpc/ConsumerApplication.java",
    "content": "package cn.iocoder.springboot.lab63.rpc;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.ImportResource;\n\n@SpringBootApplication\n@ImportResource(\"classpath:motan.xml\")\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(ConsumerApplication.class, args);;\n    }\n\n}\n"
  },
  {
    "path": "lab-63/lab-63-motan-xml-demo/lab-63-motan-xml-demo-user-rpc-service-consumer/src/main/java/cn/iocoder/springboot/lab63/rpc/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab63.rpc.controller;\n\nimport cn.iocoder.springboot.lab63.rpc.api.UserRpcService;\nimport cn.iocoder.springboot.lab63.rpc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab63.rpc.dto.UserDTO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Autowired\n    private UserRpcService userRpcService;\n\n    @GetMapping(\"/get\")\n    public UserDTO get(@RequestParam(\"id\") Integer id) {\n        return userRpcService.get(id);\n    }\n\n    @GetMapping(\"/add\") // 为了方便测试，实际使用 @PostMapping\n    public Integer add(@RequestParam(\"name\") String name,\n                       @RequestParam(\"gender\") Integer gender) {\n        UserAddDTO addDTO = new UserAddDTO().setName(name).setGender(gender);\n        return userRpcService.add(addDTO);\n    }\n\n}\n"
  },
  {
    "path": "lab-63/lab-63-motan-xml-demo/lab-63-motan-xml-demo-user-rpc-service-consumer/src/main/resources/motan.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:motan=\"http://api.weibo.com/schema/motan\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\n       http://api.weibo.com/schema/motan http://api.weibo.com/schema/motan.xsd\">\n\n    <!-- 注册中心配置。 -->\n    <motan:registry name=\"registry\" regProtocol=\"zookeeper\" address=\"127.0.0.1:2181\" connectTimeout=\"2000\"/>\n\n    <!-- Motan 协议配置。-->\n    <motan:protocol name=\"motan2\" default=\"true\"\n                    haStrategy=\"failover\" loadbalance=\"roundrobin\"\n                    maxClientConnection=\"10\" minClientConnection=\"2\"/>\n\n    <!-- Motan 服务调用方配置。 -->\n    <motan:referer id=\"userRpcService\" interface=\"cn.iocoder.springboot.lab63.rpc.api.UserRpcService\" />\n\n</beans>\n"
  },
  {
    "path": "lab-63/lab-63-motan-xml-demo/lab-63-motan-xml-demo-user-rpc-service-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-63-motan-xml-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-63-motan-xml-demo-user-rpc-service-provider</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <moton.version>1.1.8</moton.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Motan API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-63-motan-xml-demo-user-rpc-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Boot 基础 Starter 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 引入 Motan 相关依赖 -->\n        <dependency>\n            <groupId>com.weibo</groupId>\n            <artifactId>motan-core</artifactId>\n            <version>${moton.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.weibo</groupId>\n            <artifactId>motan-transport-netty4</artifactId>\n            <version>${moton.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.weibo</groupId>\n            <artifactId>motan-registry-zookeeper</artifactId>\n            <version>${moton.version}</version>\n        </dependency>\n        <dependency>\n            <groupId>com.weibo</groupId>\n            <artifactId>motan-springsupport</artifactId>\n            <version>${moton.version}</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-63/lab-63-motan-xml-demo/lab-63-motan-xml-demo-user-rpc-service-provider/src/main/java/cn/iocoder/springboot/lab63/rpc/ProviderApplication.java",
    "content": "package cn.iocoder.springboot.lab63.rpc;\n\nimport com.weibo.api.motan.common.MotanConstants;\nimport com.weibo.api.motan.util.MotanSwitcherUtil;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.ImportResource;\n\n@SpringBootApplication\n@ImportResource(\"classpath:motan.xml\")\npublic class ProviderApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(ProviderApplication.class, args);\n        // 设置 Motan 开启对外服务\n        MotanSwitcherUtil.setSwitcherValue(MotanConstants.REGISTRY_HEARTBEAT_SWITCHER, true);\n    }\n\n}\n"
  },
  {
    "path": "lab-63/lab-63-motan-xml-demo/lab-63-motan-xml-demo-user-rpc-service-provider/src/main/java/cn/iocoder/springboot/lab63/rpc/service/UserRpcServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab63.rpc.service;\n\n\nimport cn.iocoder.springboot.lab63.rpc.api.UserRpcService;\nimport cn.iocoder.springboot.lab63.rpc.dto.UserAddDTO;\nimport cn.iocoder.springboot.lab63.rpc.dto.UserDTO;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class UserRpcServiceImpl implements UserRpcService {\n\n    @Override\n    public UserDTO get(Integer id) {\n        return new UserDTO().setId(id)\n                .setName(\"没有昵称：\" + id)\n                .setGender(id % 2 + 1); // 1 - 男；2 - 女\n    }\n\n    @Override\n    public Integer add(UserAddDTO addDTO) {\n        return (int) (System.currentTimeMillis() / 1000); // 嘿嘿，随便返回一个 id\n    }\n\n}\n"
  },
  {
    "path": "lab-63/lab-63-motan-xml-demo/lab-63-motan-xml-demo-user-rpc-service-provider/src/main/resources/motan.xml",
    "content": "<beans xmlns=\"http://www.springframework.org/schema/beans\"\n       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n       xmlns:motan=\"http://api.weibo.com/schema/motan\"\n       xsi:schemaLocation=\"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\n       http://api.weibo.com/schema/motan http://api.weibo.com/schema/motan.xsd\">\n\n    <!-- 注册中心配置。 -->\n    <motan:registry name=\"registry\" regProtocol=\"zookeeper\" address=\"127.0.0.1:2181\" connectTimeout=\"2000\" />\n\n    <!-- Motan 协议配置。-->\n    <motan:protocol name=\"motan2\" default=\"true\"\n                    maxServerConnection=\"80000\" maxContentLength=\"1048576\"\n                    maxWorkerThread=\"800\" minWorkerThread=\"20\" />\n\n    <!-- Motan 服务提供方配置。 -->\n    <motan:service ref=\"userRpcServiceImpl\" interface=\"cn.iocoder.springboot.lab63.rpc.api.UserRpcService\" export=\"motan2:8001\" />\n\n</beans>\n"
  },
  {
    "path": "lab-63/lab-63-motan-xml-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-63</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-63-motan-xml-demo</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-63-motan-xml-demo-user-rpc-service-api</module>\n        <module>lab-63-motan-xml-demo-user-rpc-service-provider</module>\n        <module>lab-63-motan-xml-demo-user-rpc-service-consumer</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-63/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-63</artifactId>\n\n    <modules>\n        <module>lab-63-motan-xml-demo</module>\n        <module>lab-63-motan-annotations-demo</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-63/《芋道 Spring Boot Motan 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Motan/?github>\n"
  },
  {
    "path": "lab-64/lab-64-grpc-demo/lab-64-grpc-demo-application/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-64-grpc-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-64-grpc-demo-application</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <io.grpc.version>1.23.0</io.grpc.version>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 API 项目 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-64-grpc-demo-user-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 gRPC Netty 依赖，因为使用它作为网络库 -->\n        <dependency>\n            <groupId>io.grpc</groupId>\n            <artifactId>grpc-netty</artifactId>\n            <version>${io.grpc.version}</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-64/lab-64-grpc-demo/lab-64-grpc-demo-application/src/main/java/cn/iocoder/springboot/lab64/demo/DemoApplication.java",
    "content": "package cn.iocoder.springboot.lab64.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-64/lab-64-grpc-demo/lab-64-grpc-demo-application/src/main/java/cn/iocoder/springboot/lab64/demo/config/GrpcConfig.java",
    "content": "package cn.iocoder.springboot.lab64.demo.config;\n\nimport cn.iocoder.springboot.lab64.userservice.api.UserServiceGrpc;\nimport io.grpc.ManagedChannel;\nimport io.grpc.ManagedChannelBuilder;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class GrpcConfig {\n\n    private static final Integer GRPC_PORT = 8888;\n\n    @Bean\n    public ManagedChannel userGrpcManagedChannel() {\n        return ManagedChannelBuilder.forAddress(\"127.0.0.1\", GRPC_PORT).usePlaintext().build();\n    }\n\n    @Bean\n    public UserServiceGrpc.UserServiceBlockingStub userServiceGrpc() {\n        // 创建 ManagedChannel 对象\n        ManagedChannel userGrpcManagedChannel = this.userGrpcManagedChannel();\n        // 创建 UserServiceGrpc 对象\n        return UserServiceGrpc.newBlockingStub(userGrpcManagedChannel);\n    }\n\n}\n"
  },
  {
    "path": "lab-64/lab-64-grpc-demo/lab-64-grpc-demo-application/src/main/java/cn/iocoder/springboot/lab64/demo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab64.demo.controller;\n\nimport cn.iocoder.springboot.lab64.userservice.api.*;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private UserServiceGrpc.UserServiceBlockingStub userServiceGrpc;\n\n    @GetMapping(\"/get\")\n    public String get(@RequestParam(\"id\") Integer id) {\n        // 创建请求\n        UserGetRequest request = UserGetRequest.newBuilder().setId(id).build();\n        // 执行 gRPC 请求\n        UserGetResponse response = userServiceGrpc.get(request);\n        // 响应\n        return response.getName();\n    }\n\n    @GetMapping(\"/create\") // 为了方便测试，实际使用 @PostMapping\n    public Integer create(@RequestParam(\"name\") String name,\n                          @RequestParam(\"gender\") Integer gender) {\n        // 创建请求\n        UserCreateRequest request = UserCreateRequest.newBuilder()\n                .setName(name).setGender(gender).build();\n        // 执行 gRPC 请求\n        UserCreateResponse response = userServiceGrpc.create(request);\n        // 响应\n        return response.getId();\n    }\n\n}\n"
  },
  {
    "path": "lab-64/lab-64-grpc-demo/lab-64-grpc-demo-user-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-64-grpc-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-64-grpc-demo-user-service</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <io.grpc.version>1.30.0</io.grpc.version>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 API 项目 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-64-grpc-demo-user-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Boot 基础 Starter 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 引入 gRPC Netty 依赖，因为使用它作为网络库 -->\n        <dependency>\n            <groupId>io.grpc</groupId>\n            <artifactId>grpc-netty</artifactId>\n            <version>${io.grpc.version}</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-64/lab-64-grpc-demo/lab-64-grpc-demo-user-service/src/main/java/cn/iocoder/springboot/lab64/userservice/UserServiceApplication.java",
    "content": "package cn.iocoder.springboot.lab64.userservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\nimport java.util.concurrent.CountDownLatch;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) throws InterruptedException {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(UserServiceApplication.class, args);\n        // 阻塞，避免应用退出\n        new CountDownLatch(1).await();\n    }\n\n}\n"
  },
  {
    "path": "lab-64/lab-64-grpc-demo/lab-64-grpc-demo-user-service/src/main/java/cn/iocoder/springboot/lab64/userservice/config/GrpcConfig.java",
    "content": "package cn.iocoder.springboot.lab64.userservice.config;\n\nimport cn.iocoder.springboot.lab64.userservice.rpc.UserServiceGrpcImpl;\nimport io.grpc.Server;\nimport io.grpc.ServerBuilder;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.io.IOException;\n\n@Configuration\npublic class GrpcConfig {\n\n    private final Logger logger = LoggerFactory.getLogger(getClass());\n\n    /**\n     * gRPC Server 端口\n     */\n    private static final Integer GRPC_PORT = 8888;\n\n    @Bean\n    public Server grpcServer(final UserServiceGrpcImpl userServiceGrpc) throws IOException {\n        // 创建 gRPC Server 对象\n        Server server = ServerBuilder.forPort(GRPC_PORT)\n                .addService(userServiceGrpc)\n                .build();\n        // 启动 gRPC Server\n        server.start();\n        logger.info(\"[grpcServer][启动完成，端口为({})]\", server.getPort());\n        return server;\n    }\n\n}\n"
  },
  {
    "path": "lab-64/lab-64-grpc-demo/lab-64-grpc-demo-user-service/src/main/java/cn/iocoder/springboot/lab64/userservice/rpc/UserServiceGrpcImpl.java",
    "content": "package cn.iocoder.springboot.lab64.userservice.rpc;\n\nimport cn.iocoder.springboot.lab64.userservice.api.*;\nimport io.grpc.stub.StreamObserver;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class UserServiceGrpcImpl extends UserServiceGrpc.UserServiceImplBase {\n\n    @Override\n    public void get(UserGetRequest request, StreamObserver<UserGetResponse> responseObserver) {\n        // 创建响应对象\n        UserGetResponse.Builder builder = UserGetResponse.newBuilder();\n        builder.setId(request.getId())\n                .setName(\"没有昵称：\" + request.getId())\n                .setGender(request.getId() % 2 + 1);\n        // 返回响应\n        responseObserver.onNext(builder.build());\n        responseObserver.onCompleted();\n    }\n\n    @Override\n    public void create(UserCreateRequest request, StreamObserver<UserCreateResponse> responseObserver) {\n        // 创建响应对象\n        UserCreateResponse.Builder builder = UserCreateResponse.newBuilder();\n        builder.setId((int) (System.currentTimeMillis() / 1000));\n        // 返回响应\n        responseObserver.onNext(builder.build());\n        responseObserver.onCompleted();\n    }\n\n}\n"
  },
  {
    "path": "lab-64/lab-64-grpc-demo/lab-64-grpc-demo-user-service-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-64-grpc-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-64-grpc-demo-user-service-api</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <io.grpc.version>1.30.0</io.grpc.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <os-maven-plugin.version>1.6.2</os-maven-plugin.version>\n        <protobuf-maven-plugin.version>0.6.1</protobuf-maven-plugin.version>\n    </properties>\n\n    <dependencies>\n        <!-- 引入 gRPC Protobuf 依赖，因为使用它作为序列化库 -->\n        <dependency>\n            <groupId>io.grpc</groupId>\n            <artifactId>grpc-protobuf</artifactId>\n            <version>${io.grpc.version}</version>\n        </dependency>\n        <!-- 引入 gRPC Stub 依赖，因为使用它作为 gRPC 客户端库 -->\n        <dependency>\n            <groupId>io.grpc</groupId>\n            <artifactId>grpc-stub</artifactId>\n            <version>${io.grpc.version}</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <extensions>\n            <!-- os-maven-plugin 插件，从 OS 系统中获取参数 -->\n            <extension>\n                <groupId>kr.motd.maven</groupId>\n                <artifactId>os-maven-plugin</artifactId>\n                <version>${os-maven-plugin.version}</version>\n            </extension>\n        </extensions>\n        <plugins>\n            <!-- protobuf-maven-plugin 插件，通过 protobuf 文件，生成 Service 和 Message 类 -->\n            <plugin>\n                <groupId>org.xolstice.maven.plugins</groupId>\n                <artifactId>protobuf-maven-plugin</artifactId>\n                <version>${protobuf-maven-plugin.version}</version>\n                <configuration>\n                    <pluginId>grpc-java</pluginId>\n                    <protocArtifact>com.google.protobuf:protoc:3.9.1:exe:${os.detected.classifier}</protocArtifact>\n                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:${io.grpc.version}:exe:${os.detected.classifier}</pluginArtifact>\n                </configuration>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>compile</goal>\n                            <goal>compile-custom</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-64/lab-64-grpc-demo/lab-64-grpc-demo-user-service-api/src/main/proto/UserService.proto",
    "content": "syntax = \"proto3\";\noption java_multiple_files = true;\n\npackage cn.iocoder.springboot.lab64.userservice.api;\n\nmessage UserGetRequest {\n    int32 id = 1;\n}\n\nmessage UserGetResponse {\n    int32 id = 1;\n    string name = 2;\n    int32 gender = 3;\n}\n\nmessage UserCreateRequest {\n    string name = 1;\n    int32 gender = 2;\n}\n\nmessage UserCreateResponse {\n    int32 id = 1;\n}\n\nservice UserService {\n\n    rpc get(UserGetRequest) returns (UserGetResponse);\n\n    rpc create(UserCreateRequest) returns (UserCreateResponse);\n\n}\n"
  },
  {
    "path": "lab-64/lab-64-grpc-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-64</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-64-grpc-demo</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-64-grpc-demo-user-service</module>\n        <module>lab-64-grpc-demo-user-service-api</module>\n        <module>lab-64-grpc-demo-application</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-64/lab-64-grpc-starter/lab-64-grpc-starter-application/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-64-grpc-starter</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-64-grpc-starter-application</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 API 项目 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-64-grpc-starter-user-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 gRPC Client Starter 依赖，实现对 gRPC 的自动配置 -->\n        <dependency>\n            <groupId>net.devh</groupId>\n            <artifactId>grpc-client-spring-boot-starter</artifactId>\n            <version>2.8.0.RELEASE</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-64/lab-64-grpc-starter/lab-64-grpc-starter-application/src/main/java/cn/iocoder/springboot/lab64/demo/DemoApplication.java",
    "content": "package cn.iocoder.springboot.lab64.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-64/lab-64-grpc-starter/lab-64-grpc-starter-application/src/main/java/cn/iocoder/springboot/lab64/demo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab64.demo.controller;\n\nimport cn.iocoder.springboot.lab64.userservice.api.*;\nimport net.devh.boot.grpc.client.inject.GrpcClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @GrpcClient(\"userService\")\n    private UserServiceGrpc.UserServiceBlockingStub userServiceGrpc;\n\n    @GetMapping(\"/get\")\n    public String get(@RequestParam(\"id\") Integer id) {\n        // 创建请求\n        UserGetRequest request = UserGetRequest.newBuilder().setId(id).build();\n        // 执行 gRPC 请求\n        UserGetResponse response = userServiceGrpc.get(request);\n        // 响应\n        return response.getName();\n    }\n\n    @GetMapping(\"/create\") // 为了方便测试，实际使用 @PostMapping\n    public Integer create(@RequestParam(\"name\") String name,\n                          @RequestParam(\"gender\") Integer gender) {\n        // 创建请求\n        UserCreateRequest request = UserCreateRequest.newBuilder()\n                .setName(name).setGender(gender).build();\n        // 执行 gRPC 请求\n        UserCreateResponse response = userServiceGrpc.create(request);\n        // 响应\n        return response.getId();\n    }\n\n}\n"
  },
  {
    "path": "lab-64/lab-64-grpc-starter/lab-64-grpc-starter-application/src/main/resources/application.yml",
    "content": "grpc:\n  # gRPC 客户端配置，对应 GrpcChannelsProperties 配置类的映射\n  client:\n    userService:\n      address: 'static://127.0.0.1:8888' # 用户服务的地址\n      negotiation-type: plaintext\n"
  },
  {
    "path": "lab-64/lab-64-grpc-starter/lab-64-grpc-starter-user-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-64-grpc-starter</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-64-grpc-starter-user-service</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 API 项目 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-64-grpc-starter-user-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Boot 基础 Starter 依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 引入 gRPC Server Starter 依赖，实现对 gRPC 的自动配置 -->\n        <dependency>\n            <groupId>net.devh</groupId>\n            <artifactId>grpc-server-spring-boot-starter</artifactId>\n            <version>2.8.0.RELEASE</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-64/lab-64-grpc-starter/lab-64-grpc-starter-user-service/src/main/java/cn/iocoder/springboot/lab64/userservice/UserServiceApplication.java",
    "content": "package cn.iocoder.springboot.lab64.userservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) throws InterruptedException {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-64/lab-64-grpc-starter/lab-64-grpc-starter-user-service/src/main/java/cn/iocoder/springboot/lab64/userservice/rpc/UserServiceGrpcImpl.java",
    "content": "package cn.iocoder.springboot.lab64.userservice.rpc;\n\nimport cn.iocoder.springboot.lab64.userservice.api.*;\nimport io.grpc.stub.StreamObserver;\nimport net.devh.boot.grpc.server.service.GrpcService;\n\n@GrpcService\npublic class UserServiceGrpcImpl extends UserServiceGrpc.UserServiceImplBase {\n\n    @Override\n    public void get(UserGetRequest request, StreamObserver<UserGetResponse> responseObserver) {\n        // 创建响应对象\n        UserGetResponse.Builder builder = UserGetResponse.newBuilder();\n        builder.setId(request.getId())\n                .setName(\"没有昵称：\" + request.getId())\n                .setGender(request.getId() % 2 + 1);\n        // 返回响应\n        responseObserver.onNext(builder.build());\n        responseObserver.onCompleted();\n    }\n\n    @Override\n    public void create(UserCreateRequest request, StreamObserver<UserCreateResponse> responseObserver) {\n        // 创建响应对象\n        UserCreateResponse.Builder builder = UserCreateResponse.newBuilder();\n        builder.setId((int) (System.currentTimeMillis() / 1000));\n        // 返回响应\n        responseObserver.onNext(builder.build());\n        responseObserver.onCompleted();\n    }\n\n}\n"
  },
  {
    "path": "lab-64/lab-64-grpc-starter/lab-64-grpc-starter-user-service/src/main/resources/application.yml",
    "content": "grpc:\n  # gRPC 服务器配置，对应 GrpcServerProperties 配置类\n  server:\n    port: 8888\n"
  },
  {
    "path": "lab-64/lab-64-grpc-starter/lab-64-grpc-starter-user-service-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-64-grpc-starter</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-64-grpc-starter-user-service-api</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <io.grpc.version>1.30.0</io.grpc.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <os-maven-plugin.version>1.6.2</os-maven-plugin.version>\n        <protobuf-maven-plugin.version>0.6.1</protobuf-maven-plugin.version>\n    </properties>\n\n    <dependencies>\n        <!-- 引入 gRPC Protobuf 依赖，因为使用它作为序列化库 -->\n        <dependency>\n            <groupId>io.grpc</groupId>\n            <artifactId>grpc-protobuf</artifactId>\n            <version>${io.grpc.version}</version>\n        </dependency>\n        <!-- 引入 gRPC Stub 依赖，因为使用它作为 gRPC 客户端库 -->\n        <dependency>\n            <groupId>io.grpc</groupId>\n            <artifactId>grpc-stub</artifactId>\n            <version>${io.grpc.version}</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <extensions>\n            <!-- os-maven-plugin 插件，从 OS 系统中获取参数 -->\n            <extension>\n                <groupId>kr.motd.maven</groupId>\n                <artifactId>os-maven-plugin</artifactId>\n                <version>${os-maven-plugin.version}</version>\n            </extension>\n        </extensions>\n        <plugins>\n            <!-- protobuf-maven-plugin 插件，通过 protobuf 文件，生成 Service 和 Message 类 -->\n            <plugin>\n                <groupId>org.xolstice.maven.plugins</groupId>\n                <artifactId>protobuf-maven-plugin</artifactId>\n                <version>${protobuf-maven-plugin.version}</version>\n                <configuration>\n                    <pluginId>grpc-java</pluginId>\n                    <protocArtifact>com.google.protobuf:protoc:3.9.1:exe:${os.detected.classifier}</protocArtifact>\n                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:${io.grpc.version}:exe:${os.detected.classifier}</pluginArtifact>\n                </configuration>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>compile</goal>\n                            <goal>compile-custom</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-64/lab-64-grpc-starter/lab-64-grpc-starter-user-service-api/src/main/proto/UserService.proto",
    "content": "syntax = \"proto3\";\noption java_multiple_files = true;\n\npackage cn.iocoder.springboot.lab64.userservice.api;\n\nmessage UserGetRequest {\n    int32 id = 1;\n}\n\nmessage UserGetResponse {\n    int32 id = 1;\n    string name = 2;\n    int32 gender = 3;\n}\n\nmessage UserCreateRequest {\n    string name = 1;\n    int32 gender = 2;\n}\n\nmessage UserCreateResponse {\n    int32 id = 1;\n}\n\nservice UserService {\n\n    rpc get(UserGetRequest) returns (UserGetResponse);\n\n    rpc create(UserCreateRequest) returns (UserCreateResponse);\n\n}\n"
  },
  {
    "path": "lab-64/lab-64-grpc-starter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-64</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-64-grpc-starter</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-64-grpc-starter-user-service</module>\n        <module>lab-64-grpc-starter-user-service-api</module>\n        <module>lab-64-grpc-starter-application</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-64/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-64</artifactId>\n\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-64-grpc-demo</module>\n        <module>lab-64-grpc-starter</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-64/《芋道 Spring Boot gRPC 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/gRPC/?github>\n"
  },
  {
    "path": "lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-application/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-65-cxf-ws-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-65-cxf-ws-demo-application</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现 CXF 对 Web Services 的自动配置 -->\n        <dependency>\n            <groupId>org.apache.cxf</groupId>\n            <artifactId>cxf-spring-boot-starter-jaxws</artifactId>\n            <version>3.3.6</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <!-- cxf-codegen-plugin 插件，用于实现将 WSDL 生成目标类 -->\n            <plugin>\n                <groupId>org.apache.cxf</groupId>\n                <artifactId>cxf-codegen-plugin</artifactId>\n                <version>3.2.5</version>\n                <executions>\n                    <execution>\n                        <id>generate-sources</id>\n                        <phase>generate-sources</phase>\n                        <configuration>\n                            <!-- WSDL 源文件地址 -->\n                            <wsdlOptions>\n                                <wsdlOption>\n                                    <wsdl>src/main/resources/wsdl/user.wsdl</wsdl>\n                                    <wsdlLocation>classpath:wsdl/user.wsdl</wsdlLocation>\n                                </wsdlOption>\n                            </wsdlOptions>\n                            <!-- 生成 Java 代码目录 -->\n                            <sourceRoot>${project.build.directory}/generated/cxf</sourceRoot>\n                        </configuration>\n                        <goals>\n                            <goal>wsdl2java</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-application/src/main/java/cn/iocoder/springboot/lab65/demo/DemoApplication.java",
    "content": "package cn.iocoder.springboot.lab65.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-application/src/main/java/cn/iocoder/springboot/lab65/demo/config/CXFConfig.java",
    "content": "package cn.iocoder.springboot.lab65.demo.config;\n\nimport https.github_com.yunaiv.springboot_labs.tree.master.lab_65.lab_65_cxf_ws_demo.UserService;\nimport org.apache.cxf.jaxws.JaxWsProxyFactoryBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class CXFConfig {\n\n    @Bean\n    public UserService userService() {\n        JaxWsProxyFactoryBean jaxWsProxyFactoryBean = new JaxWsProxyFactoryBean();\n        // 设置 UserService 接口\n        jaxWsProxyFactoryBean.setServiceClass(UserService.class);\n        // 设置 Web Services 地址\n        jaxWsProxyFactoryBean.setAddress(\"http://127.0.0.1:9090/ws/user\");\n        // 创建\n        return (UserService) jaxWsProxyFactoryBean.create();\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-application/src/main/java/cn/iocoder/springboot/lab65/demo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab65.demo.controller;\n\nimport https.github_com.yunaiv.springboot_labs.tree.master.lab_65.lab_65_cxf_ws_demo.*;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private UserService userService;\n\n    @GetMapping(\"/get\")\n    public String get(@RequestParam(\"id\") Integer id) {\n        UserGetRequest request = new UserGetRequest();\n        request.setId(id);\n        // 执行 Web Services 请求\n        UserGetResponse response = userService.get(request);\n        // 响应\n        return response.getName();\n    }\n\n    @GetMapping(\"/create\") // 为了方便测试，实际使用 @PostMapping\n    public Integer create(@RequestParam(\"name\") String name,\n                          @RequestParam(\"gender\") Integer gender) {\n        // 请求\n        UserCreateRequest request = new UserCreateRequest();\n        request.setName(name);\n        request.setGender(gender);\n        // 执行 Web Services 请求\n        UserCreateResponse response = userService.create(request);\n        // 响应\n        return response.getId();\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-application/src/main/resources/wsdl/user.wsdl",
    "content": "<wsdl:definitions xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:wsdl=\"http://schemas.xmlsoap.org/wsdl/\" xmlns:tns=\"https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-65/lab-65-cxf-ws-demo\" xmlns:soap=\"http://schemas.xmlsoap.org/wsdl/soap/\" xmlns:ns1=\"http://schemas.xmlsoap.org/soap/http\" name=\"userService\" targetNamespace=\"https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-65/lab-65-cxf-ws-demo\">\n    <wsdl:types>\n        <xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:tns=\"https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-65/lab-65-cxf-ws-demo\" elementFormDefault=\"unqualified\" targetNamespace=\"https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-65/lab-65-cxf-ws-demo\" version=\"1.0\">\n            <xs:element name=\"create\" type=\"tns:create\"/>\n            <xs:element name=\"createResponse\" type=\"tns:createResponse\"/>\n            <xs:element name=\"get\" type=\"tns:get\"/>\n            <xs:element name=\"getResponse\" type=\"tns:getResponse\"/>\n            <xs:complexType name=\"create\">\n                <xs:sequence>\n                    <xs:element minOccurs=\"0\" name=\"arg0\" type=\"tns:userCreateRequest\"/>\n                </xs:sequence>\n            </xs:complexType>\n            <xs:complexType name=\"userCreateRequest\">\n                <xs:sequence>\n                    <xs:element minOccurs=\"0\" name=\"gender\" type=\"xs:int\"/>\n                    <xs:element minOccurs=\"0\" name=\"name\" type=\"xs:string\"/>\n                </xs:sequence>\n            </xs:complexType>\n            <xs:complexType name=\"createResponse\">\n                <xs:sequence>\n                    <xs:element minOccurs=\"0\" name=\"return\" type=\"tns:userCreateResponse\"/>\n                </xs:sequence>\n            </xs:complexType>\n            <xs:complexType name=\"userCreateResponse\">\n                <xs:sequence>\n                    <xs:element minOccurs=\"0\" name=\"id\" type=\"xs:int\"/>\n                </xs:sequence>\n            </xs:complexType>\n            <xs:complexType name=\"get\">\n                <xs:sequence>\n                    <xs:element minOccurs=\"0\" name=\"arg0\" type=\"tns:userGetRequest\"/>\n                </xs:sequence>\n            </xs:complexType>\n            <xs:complexType name=\"userGetRequest\">\n                <xs:sequence>\n                    <xs:element minOccurs=\"0\" name=\"id\" type=\"xs:int\"/>\n                </xs:sequence>\n            </xs:complexType>\n            <xs:complexType name=\"getResponse\">\n                <xs:sequence>\n                    <xs:element minOccurs=\"0\" name=\"return\" type=\"tns:userGetResponse\"/>\n                </xs:sequence>\n            </xs:complexType>\n            <xs:complexType name=\"userGetResponse\">\n                <xs:sequence>\n                    <xs:element minOccurs=\"0\" name=\"gender\" type=\"xs:int\"/>\n                    <xs:element minOccurs=\"0\" name=\"id\" type=\"xs:int\"/>\n                    <xs:element minOccurs=\"0\" name=\"name\" type=\"xs:string\"/>\n                </xs:sequence>\n            </xs:complexType>\n        </xs:schema>\n    </wsdl:types>\n    <wsdl:message name=\"create\">\n        <wsdl:part element=\"tns:create\" name=\"parameters\"> </wsdl:part>\n    </wsdl:message>\n    <wsdl:message name=\"createResponse\">\n        <wsdl:part element=\"tns:createResponse\" name=\"parameters\"> </wsdl:part>\n    </wsdl:message>\n    <wsdl:message name=\"get\">\n        <wsdl:part element=\"tns:get\" name=\"parameters\"> </wsdl:part>\n    </wsdl:message>\n    <wsdl:message name=\"getResponse\">\n        <wsdl:part element=\"tns:getResponse\" name=\"parameters\"> </wsdl:part>\n    </wsdl:message>\n    <wsdl:portType name=\"UserService\">\n        <wsdl:operation name=\"create\">\n            <wsdl:input message=\"tns:create\" name=\"create\"> </wsdl:input>\n            <wsdl:output message=\"tns:createResponse\" name=\"createResponse\"> </wsdl:output>\n        </wsdl:operation>\n        <wsdl:operation name=\"get\">\n            <wsdl:input message=\"tns:get\" name=\"get\"> </wsdl:input>\n            <wsdl:output message=\"tns:getResponse\" name=\"getResponse\"> </wsdl:output>\n        </wsdl:operation>\n    </wsdl:portType>\n    <wsdl:binding name=\"userServiceSoapBinding\" type=\"tns:UserService\">\n        <soap:binding style=\"document\" transport=\"http://schemas.xmlsoap.org/soap/http\"/>\n        <wsdl:operation name=\"create\">\n            <soap:operation soapAction=\"\" style=\"document\"/>\n            <wsdl:input name=\"create\">\n                <soap:body use=\"literal\"/>\n            </wsdl:input>\n            <wsdl:output name=\"createResponse\">\n                <soap:body use=\"literal\"/>\n            </wsdl:output>\n        </wsdl:operation>\n        <wsdl:operation name=\"get\">\n            <soap:operation soapAction=\"\" style=\"document\"/>\n            <wsdl:input name=\"get\">\n                <soap:body use=\"literal\"/>\n            </wsdl:input>\n            <wsdl:output name=\"getResponse\">\n                <soap:body use=\"literal\"/>\n            </wsdl:output>\n        </wsdl:operation>\n    </wsdl:binding>\n    <wsdl:service name=\"userService\">\n        <wsdl:port binding=\"tns:userServiceSoapBinding\" name=\"UserServiceImplPort\">\n            <soap:address location=\"http://127.0.0.1:9090/ws/user\"/>\n        </wsdl:port>\n    </wsdl:service>\n</wsdl:definitions>\n"
  },
  {
    "path": "lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-user-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-65-spring-ws-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-65-cxf-ws-demo-user-service</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现 CXF 对 Web Services 的自动配置 -->\n        <dependency>\n            <groupId>org.apache.cxf</groupId>\n            <artifactId>cxf-spring-boot-starter-jaxws</artifactId>\n            <version>3.3.6</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-user-service/src/main/java/cn/iocoder/springboot/lab65/userservice/UserServiceApplication.java",
    "content": "package cn.iocoder.springboot.lab65.userservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-user-service/src/main/java/cn/iocoder/springboot/lab65/userservice/config/CXFConfig.java",
    "content": "package cn.iocoder.springboot.lab65.userservice.config;\n\nimport cn.iocoder.springboot.lab65.userservice.service.UserService;\nimport org.apache.cxf.Bus;\nimport org.apache.cxf.bus.spring.SpringBus;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport javax.xml.ws.Endpoint;\n\n@Configuration\npublic class CXFConfig {\n\n    public static final String NAMESPACE_URI = \"https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-65/lab-65-cxf-ws-demo\";\n\n    @Bean(name = Bus.DEFAULT_BUS_ID)\n    public SpringBus springBus() {\n        return new SpringBus();\n    }\n\n    @Bean\n    public Endpoint userServiceEndpoint(UserService userService) {\n        Endpoint endpoint = Endpoint.create(userService);\n        endpoint.publish(\"/user\");//发布地址\n        return endpoint;\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-user-service/src/main/java/cn/iocoder/springboot/lab65/userservice/request/UserCreateRequest.java",
    "content": "package cn.iocoder.springboot.lab65.userservice.request;\n\n/**\n * 创建用户信息 Request\n */\npublic class UserCreateRequest {\n\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别别\n     */\n    private Integer gender;\n\n    public String getName() {\n        return name;\n    }\n\n    public UserCreateRequest setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserCreateRequest setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-user-service/src/main/java/cn/iocoder/springboot/lab65/userservice/request/UserGetRequest.java",
    "content": "package cn.iocoder.springboot.lab65.userservice.request;\n\n/**\n * 获得用户信息 Request\n */\npublic class UserGetRequest {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserGetRequest setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-user-service/src/main/java/cn/iocoder/springboot/lab65/userservice/response/UserCreateResponse.java",
    "content": "package cn.iocoder.springboot.lab65.userservice.response;\n\n/**\n * 创建用户信息 Response\n */\npublic class UserCreateResponse {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserCreateResponse setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-user-service/src/main/java/cn/iocoder/springboot/lab65/userservice/response/UserGetResponse.java",
    "content": "package cn.iocoder.springboot.lab65.userservice.response;\n\n/**\n * 获得用户信息 Response\n */\npublic class UserGetResponse {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserGetResponse setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserGetResponse setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserGetResponse setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-user-service/src/main/java/cn/iocoder/springboot/lab65/userservice/service/UserService.java",
    "content": "package cn.iocoder.springboot.lab65.userservice.service;\n\nimport cn.iocoder.springboot.lab65.userservice.config.CXFConfig;\nimport cn.iocoder.springboot.lab65.userservice.request.UserCreateRequest;\nimport cn.iocoder.springboot.lab65.userservice.request.UserGetRequest;\nimport cn.iocoder.springboot.lab65.userservice.response.UserCreateResponse;\nimport cn.iocoder.springboot.lab65.userservice.response.UserGetResponse;\n\nimport javax.jws.WebService;\n\n@WebService(targetNamespace = CXFConfig.NAMESPACE_URI)\npublic interface UserService {\n\n    UserGetResponse get(UserGetRequest request);\n\n    UserCreateResponse create(UserCreateRequest request);\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-user-service/src/main/java/cn/iocoder/springboot/lab65/userservice/service/UserServiceImpl.java",
    "content": "package cn.iocoder.springboot.lab65.userservice.service;\n\nimport cn.iocoder.springboot.lab65.userservice.config.CXFConfig;\nimport cn.iocoder.springboot.lab65.userservice.request.UserCreateRequest;\nimport cn.iocoder.springboot.lab65.userservice.request.UserGetRequest;\nimport cn.iocoder.springboot.lab65.userservice.response.UserCreateResponse;\nimport cn.iocoder.springboot.lab65.userservice.response.UserGetResponse;\nimport org.springframework.stereotype.Service;\n\nimport javax.jws.WebService;\n\n@Service\n@WebService(\n        serviceName = \"userService\", // 服务名称\n        targetNamespace = CXFConfig.NAMESPACE_URI // Namespace 命名空间\n)\npublic class UserServiceImpl implements UserService {\n\n    @Override\n    public UserGetResponse get(UserGetRequest request) {\n        UserGetResponse response = new UserGetResponse();\n        response.setId(request.getId());\n        response.setName(\"没有昵称：\" + request.getId());\n        response.setGender(request.getId() % 2 + 1);\n        return response;\n    }\n\n    @Override\n    public UserCreateResponse create(UserCreateRequest request) {\n        UserCreateResponse response = new UserCreateResponse();\n        response.setId((int) (System.currentTimeMillis() / 1000));\n        return response;\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-cxf-ws-demo/lab-65-cxf-ws-demo-user-service/src/main/resources/application.yml",
    "content": "# CXF 配置项，对应 CxfProperties 配置类\ncxf:\n  path: /ws/ # CXF CXFServlet 的匹配路径\n\nserver:\n  port: 9090 # 设置服务器端口为 9090\n"
  },
  {
    "path": "lab-65/lab-65-cxf-ws-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-65</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-65-cxf-ws-demo</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-65-cxf-ws-demo-user-service</module>\n        <module>lab-65-cxf-ws-demo-application</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-application/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-65-spring-ws-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-65-spring-ws-demo-application</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring WebService 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web-services</artifactId>\n        </dependency>\n\n        <!-- Java WSDL 实现库 -->\n        <dependency>\n            <groupId>wsdl4j</groupId>\n            <artifactId>wsdl4j</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <!-- maven-jaxb2-plugin 插件，用于实现将 WSDL 生成目标类 -->\n            <plugin>\n                <groupId>org.jvnet.jaxb2.maven2</groupId>\n                <artifactId>maven-jaxb2-plugin</artifactId>\n                <version>0.14.0</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>generate</goal>\n                        </goals>\n                    </execution>\n                </executions>\n                <configuration>\n                    <!-- WSDL 源文件地址 -->\n                    <schemaLanguage>WSDL</schemaLanguage>\n                    <schemas>\n                        <schema>\n                            <url>http://127.0.0.1:8080/ws/users.wsdl</url>\n                        </schema>\n                    </schemas>\n                    <!-- 生成代码目标包 -->\n                    <generatePackage>cn.iocoder.springboot.lab65.demo.wsdl</generatePackage>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-application/src/main/java/cn/iocoder/springboot/lab65/demo/DemoApplication.java",
    "content": "package cn.iocoder.springboot.lab65.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-application/src/main/java/cn/iocoder/springboot/lab65/demo/client/UserClient.java",
    "content": "package cn.iocoder.springboot.lab65.demo.client;\n\nimport cn.iocoder.springboot.lab65.demo.wsdl.UserCreateRequest;\nimport cn.iocoder.springboot.lab65.demo.wsdl.UserCreateResponse;\nimport cn.iocoder.springboot.lab65.demo.wsdl.UserGetRequest;\nimport cn.iocoder.springboot.lab65.demo.wsdl.UserGetResponse;\nimport org.springframework.ws.client.core.support.WebServiceGatewaySupport;\n\npublic class UserClient extends WebServiceGatewaySupport {\n\n    public UserGetResponse getUser(Integer id) {\n        // 创建请求对象\n        UserGetRequest request = new UserGetRequest();\n        request.setId(id);\n        // 执行请求\n        return (UserGetResponse) getWebServiceTemplate().marshalSendAndReceive(request);\n    }\n\n    public UserCreateResponse createUser(String name, Integer gender) {\n        // 创建请求对象\n        UserCreateRequest request = new UserCreateRequest();\n        request.setName(name);\n        request.setGender(gender);\n        // 执行请求\n        return (UserCreateResponse) getWebServiceTemplate().marshalSendAndReceive(request);\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-application/src/main/java/cn/iocoder/springboot/lab65/demo/config/WebServicesConfig.java",
    "content": "package cn.iocoder.springboot.lab65.demo.config;\n\nimport cn.iocoder.springboot.lab65.demo.client.UserClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.oxm.jaxb.Jaxb2Marshaller;\n\n@Configuration\npublic class WebServicesConfig {\n\n    // 创建 Jaxb2Marshaller Bean，实现 XML 与 Bean 的互相转换\n    @Bean\n    public Jaxb2Marshaller marshaller() {\n        Jaxb2Marshaller marshaller = new Jaxb2Marshaller();\n        marshaller.setContextPath(\"cn.iocoder.springboot.lab65.demo.wsdl\"); // 用户服务的 WSDL 文件\n        return marshaller;\n    }\n\n    // 创建 UserClient Bean\n    @Bean\n    public UserClient countryClient(Jaxb2Marshaller marshaller) {\n        UserClient client = new UserClient();\n        client.setDefaultUri(\"http://127.0.0.1:8080/ws\"); // 用户服务的 Web Services 地址\n        client.setMarshaller(marshaller);\n        client.setUnmarshaller(marshaller);\n        return client;\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-application/src/main/java/cn/iocoder/springboot/lab65/demo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab65.demo.controller;\n\nimport cn.iocoder.springboot.lab65.demo.client.UserClient;\nimport cn.iocoder.springboot.lab65.demo.wsdl.UserCreateResponse;\nimport cn.iocoder.springboot.lab65.demo.wsdl.UserGetResponse;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private UserClient userClient;\n\n    @GetMapping(\"/get\")\n    public String get(@RequestParam(\"id\") Integer id) {\n        // 执行 Web Services 请求\n        UserGetResponse response = userClient.getUser(id);\n        // 响应\n        return response.getName();\n    }\n\n    @GetMapping(\"/create\") // 为了方便测试，实际使用 @PostMapping\n    public Integer create(@RequestParam(\"name\") String name,\n                          @RequestParam(\"gender\") Integer gender) {\n        // 执行 Web Services 请求\n        UserCreateResponse response = userClient.createUser(name, gender);\n        // 响应\n        return response.getId();\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-application/src/main/resources/application.yml",
    "content": "server:\n  port: 9090\n"
  },
  {
    "path": "lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-user-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-65-spring-ws-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-65-spring-ws-demo-user-service</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring Web Services 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web-services</artifactId>\n        </dependency>\n\n        <!-- Java WSDL 实现库 -->\n        <dependency>\n            <groupId>wsdl4j</groupId>\n            <artifactId>wsdl4j</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <!-- jaxb2-maven-plugin 插件，用于实现将 XML 生成目标类 -->\n            <plugin>\n                <groupId>org.codehaus.mojo</groupId>\n                <artifactId>jaxb2-maven-plugin</artifactId>\n                <version>2.5.0</version>\n                <executions>\n                    <execution>\n                        <id>xjc</id>\n                        <goals>\n                            <goal>xjc</goal>\n                        </goals>\n                    </execution>\n                </executions>\n                <configuration>\n                    <!-- 源文件地址 -->\n                    <sources>\n                        <source>${project.basedir}/src/main/resources/users.xsd</source>\n                    </sources>\n                    <!-- 生成代码目标包 -->\n                    <packageName>cn.iocoder.springboot.lab65.userservice.model</packageName>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-user-service/src/main/java/cn/iocoder/springboot/lab65/userservice/UserServiceApplication.java",
    "content": "package cn.iocoder.springboot.lab65.userservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-user-service/src/main/java/cn/iocoder/springboot/lab65/userservice/config/WebServicesConfig.java",
    "content": "package cn.iocoder.springboot.lab65.userservice.config;\n\nimport org.springframework.boot.web.servlet.ServletRegistrationBean;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.io.ClassPathResource;\nimport org.springframework.ws.config.annotation.EnableWs;\nimport org.springframework.ws.config.annotation.WsConfigurerAdapter;\nimport org.springframework.ws.server.EndpointInterceptor;\nimport org.springframework.ws.transport.http.MessageDispatcherServlet;\nimport org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition;\nimport org.springframework.xml.xsd.SimpleXsdSchema;\nimport org.springframework.xml.xsd.XsdSchema;\n\nimport java.util.List;\n\n@Configuration\n@EnableWs // 开启 Web Services 服务\npublic class WebServicesConfig extends WsConfigurerAdapter {\n\n    public static final String NAMESPACE_URI = \"https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-65/lab-65-spring-ws-demo\";\n\n    @Bean\n\tpublic ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {\n\t\tMessageDispatcherServlet servlet = new MessageDispatcherServlet();\n\t\tservlet.setApplicationContext(applicationContext);\n\t\tservlet.setTransformWsdlLocations(true);\n\t\treturn new ServletRegistrationBean<>(servlet, \"/ws/*\");\n\t}\n\n    @Bean\n    public XsdSchema usersSchema() {\n        return new SimpleXsdSchema(new ClassPathResource(\"users.xsd\"));\n    }\n\n    @Bean(name = \"users\")\n    public DefaultWsdl11Definition defaultWsdl11Definition(XsdSchema usersSchema) {\n        DefaultWsdl11Definition wsdl11Definition = new DefaultWsdl11Definition();\n        wsdl11Definition.setLocationUri(\"/ws\");\n        wsdl11Definition.setTargetNamespace(NAMESPACE_URI);\n        wsdl11Definition.setSchema(usersSchema);\n        wsdl11Definition.setPortTypeName(\"UsersPort\");\n        return wsdl11Definition;\n    }\n\n    @Override\n    public void addInterceptors(List<EndpointInterceptor> interceptors) {\n        // 可自定义附加拦截器\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-user-service/src/main/java/cn/iocoder/springboot/lab65/userservice/endpoint/UserEndpoint.java",
    "content": "package cn.iocoder.springboot.lab65.userservice.endpoint;\n\nimport cn.iocoder.springboot.lab65.userservice.config.WebServicesConfig;\nimport cn.iocoder.springboot.lab65.userservice.model.UserCreateRequest;\nimport cn.iocoder.springboot.lab65.userservice.model.UserCreateResponse;\nimport cn.iocoder.springboot.lab65.userservice.model.UserGetRequest;\nimport cn.iocoder.springboot.lab65.userservice.model.UserGetResponse;\nimport org.springframework.ws.server.endpoint.annotation.Endpoint;\nimport org.springframework.ws.server.endpoint.annotation.PayloadRoot;\nimport org.springframework.ws.server.endpoint.annotation.RequestPayload;\nimport org.springframework.ws.server.endpoint.annotation.ResponsePayload;\n\n@Endpoint\npublic class UserEndpoint {\n\n\t@PayloadRoot(namespace = WebServicesConfig.NAMESPACE_URI, localPart = \"UserGetRequest\")\n\t@ResponsePayload\n\tpublic UserGetResponse get(@RequestPayload UserGetRequest request) {\n        UserGetResponse response = new UserGetResponse();\n        response.setId(request.getId());\n        response.setName(\"没有昵称：\" + request.getId());\n        response.setGender(request.getId() % 2 + 1);\n\t\treturn response;\n\t}\n\n    @PayloadRoot(namespace = WebServicesConfig.NAMESPACE_URI, localPart = \"UserCreateRequest\")\n    @ResponsePayload\n    public UserCreateResponse create(@RequestPayload UserCreateRequest request) {\n        UserCreateResponse response = new UserCreateResponse();\n        response.setId((int) (System.currentTimeMillis() / 1000));\n        return response;\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-spring-ws-demo/lab-65-spring-ws-demo-user-service/src/main/resources/users.xsd",
    "content": "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n           targetNamespace=\"https://github.com/YunaiV/SpringBoot-Labs/tree/master/lab-65/lab-65-spring-ws-demo\"\n           elementFormDefault=\"qualified\">\n\n    <!-- 获得用户请求 -->\n    <xs:element name=\"UserGetRequest\">\n        <xs:complexType>\n            <xs:sequence>\n                <xs:element name=\"id\" type=\"xs:int\"/>\n            </xs:sequence>\n        </xs:complexType>\n    </xs:element>\n    <!-- 获得用户响应 -->\n    <xs:element name=\"UserGetResponse\">\n        <xs:complexType>\n            <xs:sequence>\n                <xs:element name=\"id\" type=\"xs:int\" />\n                <xs:element name=\"name\" type=\"xs:string\"/>\n                <xs:element name=\"gender\" type=\"xs:int\"/>\n            </xs:sequence>\n        </xs:complexType>\n    </xs:element>\n\n    <!-- 创建用户请求 -->\n    <xs:element name=\"UserCreateRequest\">\n        <xs:complexType>\n            <xs:sequence>\n                <xs:element name=\"name\" type=\"xs:string\"/>\n                <xs:element name=\"gender\" type=\"xs:int\"/>\n            </xs:sequence>\n        </xs:complexType>\n    </xs:element>\n    <!-- 创建用户响应 -->\n    <xs:element name=\"UserCreateResponse\">\n        <xs:complexType>\n            <xs:sequence>\n                <xs:element name=\"id\" type=\"xs:int\"/>\n            </xs:sequence>\n        </xs:complexType>\n    </xs:element>\n\n</xs:schema>\n"
  },
  {
    "path": "lab-65/lab-65-spring-ws-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-65</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-65-spring-ws-demo</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-65-spring-ws-demo-user-service</module>\n        <module>lab-65-spring-ws-demo-application</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-65/lab-65-ws-feign-client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-65</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-65-ws-feign-client</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Feign SOAP 拓展的依赖 -->\n        <dependency>\n            <groupId>io.github.openfeign</groupId>\n            <artifactId>feign-soap</artifactId>\n            <version>11.0</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <!-- maven-jaxb2-plugin 插件，用于实现将 WSDL 生成目标类 -->\n            <plugin>\n                <groupId>org.jvnet.jaxb2.maven2</groupId>\n                <artifactId>maven-jaxb2-plugin</artifactId>\n                <version>0.14.0</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>generate</goal>\n                        </goals>\n                    </execution>\n                </executions>\n                <configuration>\n                    <!-- WSDL 源文件地址 -->\n                    <schemaLanguage>WSDL</schemaLanguage>\n                    <schemas>\n                        <schema>\n                            <url>http://127.0.0.1:8080/ws/users.wsdl</url>\n                        </schema>\n                    </schemas>\n                    <!-- 生成代码目标包 -->\n                    <generatePackage>cn.iocoder.springboot.lab65.demo.wsdl</generatePackage>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-65/lab-65-ws-feign-client/src/main/java/cn/iocoder/springboot/lab65/demo/FeignDemoApplication.java",
    "content": "package cn.iocoder.springboot.lab65.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class FeignDemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(FeignDemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-ws-feign-client/src/main/java/cn/iocoder/springboot/lab65/demo/config/FeignConfig.java",
    "content": "package cn.iocoder.springboot.lab65.demo.config;\n\nimport cn.iocoder.springboot.lab65.demo.feign.UserServiceFeignClient;\nimport feign.Feign;\nimport feign.jaxb.JAXBContextFactory;\nimport feign.soap.SOAPDecoder;\nimport feign.soap.SOAPEncoder;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class FeignConfig {\n\n    private static final JAXBContextFactory JAXB_FACTORY = new JAXBContextFactory.Builder()\n            .withMarshallerJAXBEncoding(\"UTF-8\")\n            .build();\n\n    @Bean\n    public UserServiceFeignClient userServiceFeignClient() {\n        return Feign.builder()\n                .encoder(new SOAPEncoder(JAXB_FACTORY))\n                .decoder(new SOAPDecoder(JAXB_FACTORY))\n                .target(UserServiceFeignClient.class, \"http://127.0.0.1:8080/ws\"); // 目标地址\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-ws-feign-client/src/main/java/cn/iocoder/springboot/lab65/demo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab65.demo.controller;\n\nimport cn.iocoder.springboot.lab65.demo.feign.UserServiceFeignClient;\nimport cn.iocoder.springboot.lab65.demo.wsdl.UserCreateRequest;\nimport cn.iocoder.springboot.lab65.demo.wsdl.UserCreateResponse;\nimport cn.iocoder.springboot.lab65.demo.wsdl.UserGetRequest;\nimport cn.iocoder.springboot.lab65.demo.wsdl.UserGetResponse;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private UserServiceFeignClient userClient;\n\n    @GetMapping(\"/get\")\n    public String get(@RequestParam(\"id\") Integer id) {\n        // 请求\n        UserGetRequest request = new UserGetRequest();\n        request.setId(id);\n        // 执行 Web Services 请求\n        UserGetResponse response = userClient.getUser(request);\n        // 响应\n        return response.getName();\n    }\n\n    @GetMapping(\"/create\") // 为了方便测试，实际使用 @PostMapping\n    public Integer create(@RequestParam(\"name\") String name,\n                          @RequestParam(\"gender\") Integer gender) {\n        // 请求\n        UserCreateRequest request = new UserCreateRequest();\n        request.setName(name);\n        request.setGender(gender);\n        // 执行 Web Services 请求\n        UserCreateResponse response = userClient.createUser(request);\n        // 响应\n        return response.getId();\n    }\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-ws-feign-client/src/main/java/cn/iocoder/springboot/lab65/demo/feign/UserServiceFeignClient.java",
    "content": "package cn.iocoder.springboot.lab65.demo.feign;\n\nimport cn.iocoder.springboot.lab65.demo.wsdl.UserCreateRequest;\nimport cn.iocoder.springboot.lab65.demo.wsdl.UserCreateResponse;\nimport cn.iocoder.springboot.lab65.demo.wsdl.UserGetRequest;\nimport cn.iocoder.springboot.lab65.demo.wsdl.UserGetResponse;\nimport feign.Headers;\nimport feign.RequestLine;\n\n/**\n * 用户服务 Feign Client\n */\npublic interface UserServiceFeignClient {\n\n    // 获得用户详情\n    @RequestLine(\"POST /\")\n    @Headers(\"Content-Type: text/xml\")\n    UserGetResponse getUser(UserGetRequest request);\n\n    // 创建用户\n    @RequestLine(\"POST /\")\n    @Headers(\"Content-Type: text/xml\")\n    UserCreateResponse createUser(UserCreateRequest request);\n\n}\n"
  },
  {
    "path": "lab-65/lab-65-ws-feign-client/src/main/resources/application.yml",
    "content": "server:\n  port: 9090\n"
  },
  {
    "path": "lab-65/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-65</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-65-spring-ws-demo</module>\n        <module>lab-65-ws-feign-client</module>\n        <module>lab-65-cxf-ws-demo</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-65/《芋道 Spring Boot Web Services 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Web-Services/?github>\n"
  },
  {
    "path": "lab-66/lab-66-spring-data-solr/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-66</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-66-spring-data-solr</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 自动化配置 Spring Data Solr -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-solr</artifactId>\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-66/lab-66-spring-data-solr/src/main/java/cn/iocoder/springboot/lab15/springdatasolr/Application.java",
    "content": "package cn.iocoder.springboot.lab15.springdatasolr;\n\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n}\n"
  },
  {
    "path": "lab-66/lab-66-spring-data-solr/src/main/java/cn/iocoder/springboot/lab15/springdatasolr/dataobject/SolrProductDO.java",
    "content": "package cn.iocoder.springboot.lab15.springdatasolr.dataobject;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.solr.core.mapping.Indexed;\nimport org.springframework.data.solr.core.mapping.SolrDocument;\n\n@SolrDocument(collection = \"new_core\")\npublic class SolrProductDO {\n\n    /**\n     * ID 主键\n     */\n    @Id\n    @Indexed(name = \"id\", type = \"int\")\n    private Integer id;\n\n    /**\n     * SPU 名字\n     */\n    @Indexed(name = \"name\", type = \"string\")\n    private String name;\n    /**\n     * 描述\n     */\n    @Indexed(name = \"description\", type = \"string\")\n    private String description;\n    /**\n     * 分类编号\n     */\n    @Indexed(name = \"cid\", type = \"cid\")\n    private Integer cid;\n    /**\n     * 分类名\n     */\n    @Indexed(name = \"category_name\", type = \"string\")\n    private String categoryName;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public SolrProductDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public SolrProductDO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public SolrProductDO setDescription(String description) {\n        this.description = description;\n        return this;\n    }\n\n    public Integer getCid() {\n        return cid;\n    }\n\n    public SolrProductDO setCid(Integer cid) {\n        this.cid = cid;\n        return this;\n    }\n\n    public String getCategoryName() {\n        return categoryName;\n    }\n\n    public SolrProductDO setCategoryName(String categoryName) {\n        this.categoryName = categoryName;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"SolrProductDO{\" +\n                \"id=\" + id +\n                \", name='\" + name + '\\'' +\n                \", description='\" + description + '\\'' +\n                \", cid=\" + cid +\n                \", categoryName='\" + categoryName + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-66/lab-66-spring-data-solr/src/main/java/cn/iocoder/springboot/lab15/springdatasolr/repository/ProductRepository.java",
    "content": "package cn.iocoder.springboot.lab15.springdatasolr.repository;\n\nimport cn.iocoder.springboot.lab15.springdatasolr.dataobject.SolrProductDO;\nimport org.springframework.data.solr.repository.SolrCrudRepository;\n\npublic interface ProductRepository extends SolrCrudRepository<SolrProductDO, Integer> {\n\n}\n"
  },
  {
    "path": "lab-66/lab-66-spring-data-solr/src/main/java/cn/iocoder/springboot/lab15/springdatasolr/repository/ProductRepository02.java",
    "content": "package cn.iocoder.springboot.lab15.springdatasolr.repository;\n\nimport cn.iocoder.springboot.lab15.springdatasolr.dataobject.SolrProductDO;\nimport org.springframework.data.solr.repository.SolrCrudRepository;\n\npublic interface ProductRepository02 extends SolrCrudRepository<SolrProductDO, Integer> {\n\n    SolrProductDO findByName(String name);\n\n//    Page<SolrProductDO> findByNameLike(String name, Pageable pageable);\n\n}\n"
  },
  {
    "path": "lab-66/lab-66-spring-data-solr/src/main/java/cn/iocoder/springboot/lab15/springdatasolr/repository/ProductRepository03.java",
    "content": "package cn.iocoder.springboot.lab15.springdatasolr.repository;\n\nimport cn.iocoder.springboot.lab15.springdatasolr.dataobject.SolrProductDO;\nimport org.springframework.data.solr.repository.Query;\nimport org.springframework.data.solr.repository.SolrCrudRepository;\n\nimport java.util.List;\n\npublic interface ProductRepository03 extends SolrCrudRepository<SolrProductDO, Integer> {\n\n    /**\n     * 根据 name 匹配商品名或者商品分类，获得符合的商品列表\n     */\n    @Query(\"name:?0 OR category_name:?0\")\n    List<SolrProductDO> findByCustomQuery(String name);\n\n}\n"
  },
  {
    "path": "lab-66/lab-66-spring-data-solr/src/main/resources/application.yml",
    "content": "spring:\n  data:\n    # Spring Data Solr 配置项，对应 SolrProperties 配置类\n    solr:\n      host: 'http://127.0.0.1:8983/solr' # Solr 服务器地址\n"
  },
  {
    "path": "lab-66/lab-66-spring-data-solr/src/test/java/cn/iocoder/springboot/lab15/springdatasolr/repository/ProductRepository02Test.java",
    "content": "package cn.iocoder.springboot.lab15.springdatasolr.repository;\n\nimport cn.iocoder.springboot.lab15.springdatasolr.Application;\nimport cn.iocoder.springboot.lab15.springdatasolr.dataobject.SolrProductDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class ProductRepository02Test {\n\n    @Autowired\n    private ProductRepository02 productRepository;\n\n    @Test // 根据名字获得一条记录\n    public void testFindByName() {\n        SolrProductDO product = productRepository.findByName(\"芋道源码\");\n        System.out.println(product);\n    }\n\n//    @Test // 使用 name 模糊查询，分页返回结果\n//    public void testFindByNameLike() {\n//        // 根据情况，是否要制造测试数据\n//        if (false) {\n//            testInsert();\n//        }\n//\n//        // 创建排序条件\n//        Sort sort = Sort.by(Sort.Direction.DESC, \"id\");  // ID 倒序\n//        // 创建分页条件。\n//        Pageable pageable = PageRequest.of(0, 10, sort);\n//        // 执行分页操作\n//        Page<SolrProductDO> page = productRepository.findByNameLike(\"芋道\", pageable);\n//        // 打印\n//        System.out.println(page.getTotalElements());\n//        System.out.println(page.getTotalPages());\n//    }\n//\n//    /**\n//     * 为了给分页制造一点数据\n//     */\n//    private void testInsert() {\n//        for (int i = 1; i <= 100; i++) {\n//            SolrProductDO product = new SolrProductDO();\n//            product.setId(i); // 一般 ES 的 ID 编号，使用 DB 数据对应的编号。这里，先写死\n//            product.setName(\"芋道源码：\" + i);\n//            product.setDescription(\"我只是一个描述\");\n//            product.setCid(1);\n//            product.setCategoryName(\"技术\");\n//            productRepository.save(product);\n//        }\n//    }\n\n}\n"
  },
  {
    "path": "lab-66/lab-66-spring-data-solr/src/test/java/cn/iocoder/springboot/lab15/springdatasolr/repository/ProductRepository03Test.java",
    "content": "package cn.iocoder.springboot.lab15.springdatasolr.repository;\n\nimport cn.iocoder.springboot.lab15.springdatasolr.Application;\nimport cn.iocoder.springboot.lab15.springdatasolr.dataobject.SolrProductDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.List;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class ProductRepository03Test {\n\n    @Autowired\n    private ProductRepository03 productRepository;\n\n    @Test\n    public void testFindByCustomQuery() {\n        List<SolrProductDO> products = productRepository.findByCustomQuery(\"技术\");\n        System.out.println(products.size());\n    }\n\n}\n"
  },
  {
    "path": "lab-66/lab-66-spring-data-solr/src/test/java/cn/iocoder/springboot/lab15/springdatasolr/repository/ProductRepositoryTest.java",
    "content": "package cn.iocoder.springboot.lab15.springdatasolr.repository;\n\nimport cn.iocoder.springboot.lab15.springdatasolr.Application;\nimport cn.iocoder.springboot.lab15.springdatasolr.dataobject.SolrProductDO;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\nimport java.util.Arrays;\nimport java.util.Optional;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(classes = Application.class)\npublic class ProductRepositoryTest {\n\n    @Autowired\n    private ProductRepository productRepository;\n\n    @Test // 插入一条记录\n    public void testInsert() {\n        SolrProductDO product = new SolrProductDO();\n        product.setId(1); // 一般 Solr 的 ID 编号，使用 DB 数据对应的编号。这里，先写死\n        product.setName(\"芋道源码\");\n        product.setDescription(\"我只是一个描述\");\n        product.setCid(2);\n        product.setCategoryName(\"技术\");\n        productRepository.save(product);\n    }\n\n    // 这里要注意，如果使用 save 方法来更新的话，必须是全量字段，否则其它字段会被覆盖。\n    // 所以，这里仅仅是作为一个示例。\n    @Test // 更新一条记录\n    public void testUpdate() {\n        SolrProductDO product = new SolrProductDO();\n        product.setId(1);\n        product.setCid(2);\n        product.setCategoryName(\"技术-Java\");\n        productRepository.save(product);\n    }\n\n    @Test // 根据 ID 编号，删除一条记录\n    public void testDelete() {\n        productRepository.deleteById(1);\n    }\n\n    @Test // 根据 ID 编号，查询一条记录\n    public void testSelectById() {\n        Optional<SolrProductDO> userDO = productRepository.findById(1);\n        System.out.println(userDO.isPresent());\n    }\n\n    @Test // 根据 ID 编号数组，查询多条记录\n    public void testSelectByIds() {\n        Iterable<SolrProductDO> users = productRepository.findAllById(Arrays.asList(1, 4));\n        users.forEach(System.out::println);\n    }\n\n}\n"
  },
  {
    "path": "lab-66/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-66</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-66-spring-data-solr</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-66/《芋道 Spring Boot Web Solr 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Solr/?github>\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-67-netty-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-67-netty-demo-client</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- Netty 依赖 -->\n        <dependency>\n            <groupId>io.netty</groupId>\n            <artifactId>netty-all</artifactId>\n            <version>4.1.50.Final</version>\n        </dependency>\n\n        <!-- 引入 netty-demo-common 封装 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-67-netty-demo-common</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/NettyClientApplication.java",
    "content": "package cn.iocoder.springboot.lab67.nettyclientdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class NettyClientApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(NettyClientApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/client/NettyClient.java",
    "content": "package cn.iocoder.springboot.lab67.nettyclientdemo.client;\n\nimport cn.iocoder.springboot.lab67.nettyclientdemo.client.handler.NettyClientHandlerInitializer;\nimport cn.iocoder.springboot.lab67.nettycommondemo.codec.Invocation;\nimport io.netty.bootstrap.Bootstrap;\nimport io.netty.channel.*;\nimport io.netty.channel.nio.NioEventLoopGroup;\nimport io.netty.channel.socket.nio.NioSocketChannel;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.PostConstruct;\nimport javax.annotation.PreDestroy;\nimport java.util.concurrent.TimeUnit;\n\n@Component\npublic class NettyClient {\n\n    /**\n     * 重连频率，单位：秒\n     */\n    private static final Integer RECONNECT_SECONDS = 20;\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Value(\"${netty.server.host}\")\n    private String serverHost;\n    @Value(\"${netty.server.port}\")\n    private Integer serverPort;\n\n    @Autowired\n    private NettyClientHandlerInitializer nettyClientHandlerInitializer;\n\n    /**\n     * 线程组，用于客户端对服务端的链接、数据读写\n     */\n    private EventLoopGroup eventGroup = new NioEventLoopGroup();\n    /**\n     * Netty Client Channel\n     */\n    private volatile Channel channel;\n\n    /**\n     * 启动 Netty Client\n     */\n    @PostConstruct\n    public void start() throws InterruptedException {\n        // 创建 Bootstrap 对象，用于 Netty Client 启动\n        Bootstrap bootstrap = new Bootstrap();\n        // 设置 Bootstrap 的各种属性。\n        bootstrap.group(eventGroup) // 设置一个 EventLoopGroup 对象\n                .channel(NioSocketChannel.class)  // 指定 Channel 为客户端 NioSocketChannel\n                .remoteAddress(serverHost, serverPort) // 指定链接服务器的地址\n                .option(ChannelOption.SO_KEEPALIVE, true) // TCP Keepalive 机制，实现 TCP 层级的心跳保活功能\n                .option(ChannelOption.TCP_NODELAY, true) // 允许较小的数据包的发送，降低延迟\n                .handler(nettyClientHandlerInitializer);\n        // 链接服务器，并异步等待成功，即启动客户端\n        bootstrap.connect().addListener(new ChannelFutureListener() {\n\n            @Override\n            public void operationComplete(ChannelFuture future) throws Exception {\n                // 连接失败\n                if (!future.isSuccess()) {\n                    logger.error(\"[start][Netty Client 连接服务器({}:{}) 失败]\", serverHost, serverPort);\n                    reconnect();\n                    return;\n                }\n                // 连接成功\n                channel = future.channel();\n                logger.info(\"[start][Netty Client 连接服务器({}:{}) 成功]\", serverHost, serverPort);\n            }\n\n        });\n    }\n\n    public void reconnect() {\n        eventGroup.schedule(new Runnable() {\n            @Override\n            public void run() {\n                logger.info(\"[reconnect][开始重连]\");\n                try {\n                    start();\n                } catch (InterruptedException e) {\n                    logger.error(\"[reconnect][重连失败]\", e);\n                }\n            }\n        }, RECONNECT_SECONDS, TimeUnit.SECONDS);\n        logger.info(\"[reconnect][{} 秒后将发起重连]\", RECONNECT_SECONDS);\n    }\n\n    /**\n     * 关闭 Netty Server\n     */\n    @PreDestroy\n    public void shutdown() {\n        // 关闭 Netty Client\n        if (channel != null) {\n            channel.close();\n        }\n        // 优雅关闭一个 EventLoopGroup 对象\n        eventGroup.shutdownGracefully();\n    }\n\n    /**\n     * 发送消息\n     *\n     * @param invocation 消息体\n     */\n    public void send(Invocation invocation) {\n        if (channel == null) {\n            logger.error(\"[send][连接不存在]\");\n            return;\n        }\n        if (!channel.isActive()) {\n            logger.error(\"[send][连接({})未激活]\", channel.id());\n            return;\n        }\n        // 发送消息\n        channel.writeAndFlush(invocation);\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/client/handler/NettyClientHandler.java",
    "content": "package cn.iocoder.springboot.lab67.nettyclientdemo.client.handler;\n\nimport cn.iocoder.springboot.lab67.nettyclientdemo.client.NettyClient;\nimport cn.iocoder.springboot.lab67.nettyclientdemo.message.heartbeat.HeartbeatRequest;\nimport cn.iocoder.springboot.lab67.nettycommondemo.codec.Invocation;\nimport io.netty.channel.ChannelFutureListener;\nimport io.netty.channel.ChannelHandler;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.channel.ChannelInboundHandlerAdapter;\nimport io.netty.handler.timeout.IdleStateEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ChannelHandler.Sharable\npublic class NettyClientHandler extends ChannelInboundHandlerAdapter {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private NettyClient nettyClient;\n\n    @Override\n    public void channelInactive(ChannelHandlerContext ctx) throws Exception {\n        // 发起重连\n        nettyClient.reconnect();\n        // 继续触发事件\n        super.channelInactive(ctx);\n    }\n\n    @Override\n    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {\n        logger.error(\"[exceptionCaught][连接({}) 发生异常]\", ctx.channel().id(), cause);\n        // 断开连接\n        ctx.channel().close();\n    }\n\n    @Override\n    public void userEventTriggered(ChannelHandlerContext ctx, Object event) throws Exception {\n        // 空闲时，向服务端发起一次心跳\n        if (event instanceof IdleStateEvent) {\n            logger.info(\"[userEventTriggered][发起一次心跳]\");\n            HeartbeatRequest heartbeatRequest = new HeartbeatRequest();\n            ctx.writeAndFlush(new Invocation(HeartbeatRequest.TYPE, heartbeatRequest))\n                    .addListener(ChannelFutureListener.CLOSE_ON_FAILURE);\n        } else {\n            super.userEventTriggered(ctx, event);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/client/handler/NettyClientHandlerInitializer.java",
    "content": "package cn.iocoder.springboot.lab67.nettyclientdemo.client.handler;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.codec.InvocationDecoder;\nimport cn.iocoder.springboot.lab67.nettycommondemo.codec.InvocationEncoder;\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageDispatcher;\nimport io.netty.channel.Channel;\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.handler.timeout.IdleStateHandler;\nimport io.netty.handler.timeout.ReadTimeoutHandler;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class NettyClientHandlerInitializer extends ChannelInitializer<Channel> {\n\n    /**\n     * 心跳超时时间\n     */\n    private static final Integer READ_TIMEOUT_SECONDS = 60;\n\n    @Autowired\n    private MessageDispatcher messageDispatcher;\n\n    @Autowired\n    private NettyClientHandler nettyClientHandler;\n\n    @Override\n    protected void initChannel(Channel ch) {\n        ch.pipeline()\n                // 空闲检测\n                .addLast(new IdleStateHandler(READ_TIMEOUT_SECONDS, 0, 0))\n                .addLast(new ReadTimeoutHandler(3 * READ_TIMEOUT_SECONDS))\n                // 编码器\n                .addLast(new InvocationEncoder())\n                // 解码器\n                .addLast(new InvocationDecoder())\n                // 消息分发器\n                .addLast(messageDispatcher)\n                // 客户端处理器\n                .addLast(nettyClientHandler)\n        ;\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/config/NettyClientConfig.java",
    "content": "package cn.iocoder.springboot.lab67.nettyclientdemo.config;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageDispatcher;\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandlerContainer;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class NettyClientConfig {\n\n    @Bean\n    public MessageDispatcher messageDispatcher() {\n        return new MessageDispatcher();\n    }\n\n    @Bean\n    public MessageHandlerContainer messageHandlerContainer() {\n        return new MessageHandlerContainer();\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/controller/TestController.java",
    "content": "package cn.iocoder.springboot.lab67.nettyclientdemo.controller;\n\nimport cn.iocoder.springboot.lab67.nettyclientdemo.client.NettyClient;\nimport cn.iocoder.springboot.lab67.nettycommondemo.codec.Invocation;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/test\")\npublic class TestController {\n\n    @Autowired\n    private NettyClient nettyClient;\n\n    @PostMapping(\"/mock\")\n    public String mock(String type, String message) {\n        // 创建 Invocation 对象\n        Invocation invocation = new Invocation(type, message);\n        // 发送消息\n        nettyClient.send(invocation);\n        return \"success\";\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/message/auth/AuthRequest.java",
    "content": "package cn.iocoder.springboot.lab67.nettyclientdemo.message.auth;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message;\n\n/**\n * 用户认证请求\n */\npublic class AuthRequest implements Message {\n\n    public static final String TYPE = \"AUTH_REQUEST\";\n\n    /**\n     * 认证 Token\n     */\n    private String accessToken;\n\n    public String getAccessToken() {\n        return accessToken;\n    }\n\n    public AuthRequest setAccessToken(String accessToken) {\n        this.accessToken = accessToken;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"AuthRequest{\" +\n                \"accessToken='\" + accessToken + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/message/auth/AuthResponse.java",
    "content": "package cn.iocoder.springboot.lab67.nettyclientdemo.message.auth;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message;\n\n/**\n * 用户认证响应\n */\npublic class AuthResponse implements Message {\n\n    public static final String TYPE = \"AUTH_RESPONSE\";\n\n    /**\n     * 响应状态码\n     */\n    private Integer code;\n    /**\n     * 响应提示\n     */\n    private String message;\n\n    public Integer getCode() {\n        return code;\n    }\n\n    public AuthResponse setCode(Integer code) {\n        this.code = code;\n        return this;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public AuthResponse setMessage(String message) {\n        this.message = message;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"AuthResponse{\" +\n                \"code=\" + code +\n                \", message='\" + message + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/message/chat/ChatRedirectToUserRequest.java",
    "content": "package cn.iocoder.springboot.lab67.nettyclientdemo.message.chat;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message;\n\n/**\n * 转发消息给一个用户的 Message\n */\npublic class ChatRedirectToUserRequest implements Message {\n\n    public static final String TYPE = \"CHAT_REDIRECT_TO_USER_REQUEST\";\n\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 内容\n     */\n    private String content;\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public ChatRedirectToUserRequest setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n    public String getContent() {\n        return content;\n    }\n\n    public ChatRedirectToUserRequest setContent(String content) {\n        this.content = content;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ChatRedirectToUserRequest{\" +\n                \"msgId='\" + msgId + '\\'' +\n                \", content='\" + content + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/message/chat/ChatSendResponse.java",
    "content": "package cn.iocoder.springboot.lab67.nettyclientdemo.message.chat;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message;\n\n/**\n * 聊天发送消息结果的 Response\n */\npublic class ChatSendResponse implements Message {\n\n    public static final String TYPE = \"CHAT_SEND_RESPONSE\";\n\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 响应状态码\n     */\n    private Integer code;\n    /**\n     * 响应提示\n     */\n    private String message;\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public ChatSendResponse setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n    public Integer getCode() {\n        return code;\n    }\n\n    public ChatSendResponse setCode(Integer code) {\n        this.code = code;\n        return this;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public ChatSendResponse setMessage(String message) {\n        this.message = message;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ChatSendResponse{\" +\n                \"msgId='\" + msgId + '\\'' +\n                \", code=\" + code +\n                \", message='\" + message + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/message/chat/ChatSendToAllRequest.java",
    "content": "package cn.iocoder.springboot.lab67.nettyclientdemo.message.chat;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message;\n\n/**\n * 发送给所有人的群聊消息的 Message\n */\npublic class ChatSendToAllRequest implements Message {\n\n    public static final String TYPE = \"CHAT_SEND_TO_ALL_REQUEST\";\n\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 内容\n     */\n    private String content;\n\n    public String getContent() {\n        return content;\n    }\n\n    public ChatSendToAllRequest setContent(String content) {\n        this.content = content;\n        return this;\n    }\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public ChatSendToAllRequest setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ChatSendToAllRequest{\" +\n                \"msgId='\" + msgId + '\\'' +\n                \", content='\" + content + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/message/chat/ChatSendToOneRequest.java",
    "content": "package cn.iocoder.springboot.lab67.nettyclientdemo.message.chat;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message;\n\n/**\n * 发送给指定人的私聊消息 Request\n */\npublic class ChatSendToOneRequest implements Message {\n\n    public static final String TYPE = \"CHAT_SEND_TO_ONE_REQUEST\";\n\n    /**\n     * 发送给的用户\n     */\n    private String toUser;\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 内容\n     */\n    private String content;\n\n    public String getToUser() {\n        return toUser;\n    }\n\n    public ChatSendToOneRequest setToUser(String toUser) {\n        this.toUser = toUser;\n        return this;\n    }\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public ChatSendToOneRequest setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n    public String getContent() {\n        return content;\n    }\n\n    public ChatSendToOneRequest setContent(String content) {\n        this.content = content;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ChatSendToOneRequest{\" +\n                \"toUser='\" + toUser + '\\'' +\n                \", msgId='\" + msgId + '\\'' +\n                \", content='\" + content + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/message/heartbeat/HeartbeatRequest.java",
    "content": "package cn.iocoder.springboot.lab67.nettyclientdemo.message.heartbeat;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message;\n\n/**\n * 消息 - 心跳请求\n */\npublic class HeartbeatRequest implements Message {\n\n    /**\n     * 类型 - 心跳请求\n     */\n    public static final String TYPE = \"HEARTBEAT_REQUEST\";\n\n    @Override\n    public String toString() {\n        return \"HeartbeatRequest{}\";\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/message/heartbeat/HeartbeatResponse.java",
    "content": "package cn.iocoder.springboot.lab67.nettyclientdemo.message.heartbeat;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message;\n\n/**\n * 消息 - 心跳响应\n */\npublic class HeartbeatResponse implements Message {\n\n    /**\n     * 类型 - 心跳响应\n     */\n    public static final String TYPE = \"HEARTBEAT_RESPONSE\";\n\n    @Override\n    public String toString() {\n        return \"HeartbeatResponse{}\";\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/messagehandler/auth/AuthResponseHandler.java",
    "content": "package cn.iocoder.springboot.lab67.nettyclientdemo.messagehandler.auth;\n\nimport cn.iocoder.springboot.lab67.nettyclientdemo.message.auth.AuthResponse;\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandler;\nimport io.netty.channel.Channel;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class AuthResponseHandler implements MessageHandler<AuthResponse> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void execute(Channel channel, AuthResponse message) {\n        logger.info(\"[execute][认证结果：{}]\", message);\n    }\n\n    @Override\n    public String getType() {\n        return AuthResponse.TYPE;\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/messagehandler/chat/ChatRedirectToUserRequestHandler.java",
    "content": "package cn.iocoder.springboot.lab67.nettyclientdemo.messagehandler.chat;\n\nimport cn.iocoder.springboot.lab67.nettyclientdemo.message.chat.ChatRedirectToUserRequest;\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandler;\nimport io.netty.channel.Channel;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ChatRedirectToUserRequestHandler implements MessageHandler<ChatRedirectToUserRequest> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void execute(Channel channel, ChatRedirectToUserRequest message) {\n        logger.info(\"[execute][收到消息：{}]\", message);\n    }\n\n    @Override\n    public String getType() {\n        return ChatRedirectToUserRequest.TYPE;\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/messagehandler/chat/ChatSendResponseHandler.java",
    "content": "package cn.iocoder.springboot.lab67.nettyclientdemo.messagehandler.chat;\n\nimport cn.iocoder.springboot.lab67.nettyclientdemo.message.chat.ChatSendResponse;\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandler;\nimport io.netty.channel.Channel;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ChatSendResponseHandler implements MessageHandler<ChatSendResponse> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void execute(Channel channel, ChatSendResponse message) {\n        logger.info(\"[execute][发送结果：{}]\", message);\n    }\n\n    @Override\n    public String getType() {\n        return ChatSendResponse.TYPE;\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/java/cn/iocoder/springboot/lab67/nettyclientdemo/messagehandler/heartbeat/HeartbeatResponseHandler.java",
    "content": "package cn.iocoder.springboot.lab67.nettyclientdemo.messagehandler.heartbeat;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandler;\nimport cn.iocoder.springboot.lab67.nettyclientdemo.message.heartbeat.HeartbeatResponse;\nimport io.netty.channel.Channel;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class HeartbeatResponseHandler implements MessageHandler<HeartbeatResponse> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void execute(Channel channel, HeartbeatResponse message) {\n        logger.info(\"[execute][收到连接({}) 的心跳响应]\", channel.id());\n    }\n\n    @Override\n    public String getType() {\n        return HeartbeatResponse.TYPE;\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-client/src/main/resources/application.yml",
    "content": "netty:\n  server:\n    host: 127.0.0.1 # Netty Server 地址\n    port: 8888 # Netty Server 端口\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-common/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-67-netty-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-67-netty-demo-common</artifactId>\n\n    <properties>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencies>\n        <!-- Netty 依赖 -->\n        <dependency>\n            <groupId>io.netty</groupId>\n            <artifactId>netty-all</artifactId>\n            <version>4.1.50.Final</version>\n        </dependency>\n\n        <!-- FastJSON 依赖 -->\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>fastjson</artifactId>\n            <version>1.2.71</version>\n        </dependency>\n\n        <!-- 引入 Spring 相关依赖 -->\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-aop</artifactId>\n            <version>5.2.5.RELEASE</version>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-context</artifactId>\n            <version>5.2.5.RELEASE</version>\n        </dependency>\n\n        <!-- 引入 SLF4J 依赖 -->\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-api</artifactId>\n            <version>1.7.30</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-common/src/main/java/cn/iocoder/springboot/lab67/nettycommondemo/codec/Invocation.java",
    "content": "package cn.iocoder.springboot.lab67.nettycommondemo.codec;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message;\nimport com.alibaba.fastjson.JSON;\n\n/**\n * 通信协议的消息体\n */\npublic class Invocation {\n\n    /**\n     * 类型\n     */\n    private String type;\n    /**\n     * 消息，JSON 格式\n     */\n    private String message;\n\n    // 空构造方法\n    public Invocation() {\n    }\n\n    public Invocation(String type, String message) {\n        this.type = type;\n        this.message = message;\n    }\n\n    public Invocation(String type, Message message) {\n        this.type = type;\n        this.message = JSON.toJSONString(message);\n    }\n\n    public String getType() {\n        return type;\n    }\n\n    public Invocation setType(String type) {\n        this.type = type;\n        return this;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public Invocation setMessage(String message) {\n        this.message = message;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"Invocation{\" +\n                \"type='\" + type + '\\'' +\n                \", message='\" + message + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-common/src/main/java/cn/iocoder/springboot/lab67/nettycommondemo/codec/InvocationDecoder.java",
    "content": "package cn.iocoder.springboot.lab67.nettycommondemo.codec;\n\nimport com.alibaba.fastjson.JSON;\nimport io.netty.buffer.ByteBuf;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.handler.codec.ByteToMessageDecoder;\nimport io.netty.handler.codec.CorruptedFrameException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.List;\n\n/**\n * {@link Invocation} 解码器\n */\npublic class InvocationDecoder extends ByteToMessageDecoder {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {\n        // 标记当前读取位置\n        in.markReaderIndex();\n        // 判断是否能够读取 length 长度\n        if (in.readableBytes() <= 4) {\n            return;\n        }\n        // 读取长度\n        int length = in.readInt();\n        if (length < 0) {\n            throw new CorruptedFrameException(\"negative length: \" + length);\n        }\n        // 如果 message 不够可读，则退回到原读取位置\n        if (in.readableBytes() < length) {\n            in.resetReaderIndex();\n            return;\n        }\n        // 读取内容\n        byte[] content = new byte[length];\n        in.readBytes(content);\n        // 解析成 Invocation\n        Invocation invocation = JSON.parseObject(content, Invocation.class);\n        out.add(invocation);\n        logger.info(\"[decode][连接({}) 解析到一条消息({})]\", ctx.channel().id(), invocation.toString());\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-common/src/main/java/cn/iocoder/springboot/lab67/nettycommondemo/codec/InvocationEncoder.java",
    "content": "package cn.iocoder.springboot.lab67.nettycommondemo.codec;\n\nimport com.alibaba.fastjson.JSON;\nimport io.netty.buffer.ByteBuf;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.handler.codec.MessageToByteEncoder;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * {@link Invocation} 编码器\n */\npublic class InvocationEncoder extends MessageToByteEncoder<Invocation> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    protected void encode(ChannelHandlerContext ctx, Invocation invocation, ByteBuf out) {\n        // 将 Invocation 转换成 byte[] 数组\n        byte[] content = JSON.toJSONBytes(invocation);\n        // 写入 length\n        out.writeInt(content.length);\n        // 写入内容\n        out.writeBytes(content);\n        logger.info(\"[encode][连接({}) 编码了一条消息({})]\", ctx.channel().id(), invocation.toString());\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-common/src/main/java/cn/iocoder/springboot/lab67/nettycommondemo/dispatcher/Message.java",
    "content": "package cn.iocoder.springboot.lab67.nettycommondemo.dispatcher;\n\n/**\n * 消息接口\n */\npublic interface Message {\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-common/src/main/java/cn/iocoder/springboot/lab67/nettycommondemo/dispatcher/MessageDispatcher.java",
    "content": "package cn.iocoder.springboot.lab67.nettycommondemo.dispatcher;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.codec.Invocation;\nimport com.alibaba.fastjson.JSON;\nimport io.netty.channel.ChannelHandler;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.channel.SimpleChannelInboundHandler;\nimport org.springframework.beans.factory.annotation.Autowired;\n\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\n\n@ChannelHandler.Sharable\npublic class MessageDispatcher extends SimpleChannelInboundHandler<Invocation> {\n\n    @Autowired\n    private MessageHandlerContainer messageHandlerContainer;\n\n    private final ExecutorService executor =  Executors.newFixedThreadPool(200);\n\n    @Override\n    protected void channelRead0(ChannelHandlerContext ctx, Invocation invocation) {\n        // 获得 type 对应的 MessageHandler 处理器\n        MessageHandler messageHandler = messageHandlerContainer.getMessageHandler(invocation.getType());\n        // 获得  MessageHandler 处理器 的消息类\n        Class<? extends Message> messageClass = MessageHandlerContainer.getMessageClass(messageHandler);\n        // 解析消息\n        Message message = JSON.parseObject(invocation.getMessage(), messageClass);\n        // 执行逻辑\n        executor.submit(new Runnable() {\n\n            @Override\n            public void run() {\n                // noinspection unchecked\n                messageHandler.execute(ctx.channel(), message);\n            }\n\n        });\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-common/src/main/java/cn/iocoder/springboot/lab67/nettycommondemo/dispatcher/MessageHandler.java",
    "content": "package cn.iocoder.springboot.lab67.nettycommondemo.dispatcher;\n\nimport io.netty.channel.Channel;\n\npublic interface MessageHandler<T extends Message> {\n\n    /**\n     * 执行处理消息\n     *\n     * @param channel 通道\n     * @param message 消息\n     */\n    void execute(Channel channel, T message);\n\n    /**\n     * @return 消息类型，即每个 Message 实现类上的 TYPE 静态字段\n     */\n    String getType();\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-common/src/main/java/cn/iocoder/springboot/lab67/nettycommondemo/dispatcher/MessageHandlerContainer.java",
    "content": "package cn.iocoder.springboot.lab67.nettycommondemo.dispatcher;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.aop.framework.AopProxyUtils;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.ApplicationContext;\n\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Objects;\n\npublic class MessageHandlerContainer implements InitializingBean {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    /**\n     * 消息类型与 MessageHandler 的映射\n     */\n    private final Map<String, MessageHandler> handlers = new HashMap<>();\n\n    @Autowired\n    private ApplicationContext applicationContext;\n\n    @Override\n    public void afterPropertiesSet() throws Exception {\n        // 通过 ApplicationContext 获得所有 MessageHandler Bean\n        applicationContext.getBeansOfType(MessageHandler.class).values() // 获得所有 MessageHandler Bean\n                .forEach(messageHandler -> handlers.put(messageHandler.getType(), messageHandler)); // 添加到 handlers 中\n        logger.info(\"[afterPropertiesSet][消息处理器数量：{}]\", handlers.size());\n    }\n\n    /**\n     * 获得类型对应的 MessageHandler\n     *\n     * @param type 类型\n     * @return MessageHandler\n     */\n    MessageHandler getMessageHandler(String type) {\n        MessageHandler handler = handlers.get(type);\n        if (handler == null) {\n            throw new IllegalArgumentException(String.format(\"类型(%s) 找不到匹配的 MessageHandler 处理器\", type));\n        }\n        return handler;\n    }\n\n    /**\n     * 获得 MessageHandler 处理的消息类\n     *\n     * @param handler 处理器\n     * @return 消息类\n     */\n    static Class<? extends Message> getMessageClass(MessageHandler handler) {\n        // 获得 Bean 对应的 Class 类名。因为有可能被 AOP 代理过。\n        Class<?> targetClass = AopProxyUtils.ultimateTargetClass(handler);\n        // 获得接口的 Type 数组\n        Type[] interfaces = targetClass.getGenericInterfaces();\n        Class<?> superclass = targetClass.getSuperclass();\n        while ((Objects.isNull(interfaces) || 0 == interfaces.length) && Objects.nonNull(superclass)) { // 此处，是以父类的接口为准\n            interfaces = superclass.getGenericInterfaces();\n            superclass = targetClass.getSuperclass();\n        }\n        if (Objects.nonNull(interfaces)) {\n            // 遍历 interfaces 数组\n            for (Type type : interfaces) {\n                // 要求 type 是泛型参数\n                if (type instanceof ParameterizedType) {\n                    ParameterizedType parameterizedType = (ParameterizedType) type;\n                    // 要求是 MessageHandler 接口\n                    if (Objects.equals(parameterizedType.getRawType(), MessageHandler.class)) {\n                        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();\n                        // 取首个元素\n                        if (Objects.nonNull(actualTypeArguments) && actualTypeArguments.length > 0) {\n                            return (Class<Message>) actualTypeArguments[0];\n                        } else {\n                            throw new IllegalStateException(String.format(\"类型(%s) 获得不到消息类型\", handler));\n                        }\n                    }\n                }\n            }\n        }\n        throw new IllegalStateException(String.format(\"类型(%s) 获得不到消息类型\", handler));\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-67-netty-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-67-netty-demo-server</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- Spring Boot 基础依赖 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- Netty 依赖 -->\n        <dependency>\n            <groupId>io.netty</groupId>\n            <artifactId>netty-all</artifactId>\n            <version>4.1.50.Final</version>\n        </dependency>\n\n        <!-- 引入 netty-demo-common 封装 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>lab-67-netty-demo-common</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/NettyServerApplication.java",
    "content": "package cn.iocoder.springboot.lab67.nettyserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class NettyServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(NettyServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/config/NettyServerConfig.java",
    "content": "package cn.iocoder.springboot.lab67.nettyserverdemo.config;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageDispatcher;\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandlerContainer;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class NettyServerConfig {\n\n    @Bean\n    public MessageDispatcher messageDispatcher() {\n        return new MessageDispatcher();\n    }\n\n    @Bean\n    public MessageHandlerContainer messageHandlerContainer() {\n        return new MessageHandlerContainer();\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/message/auth/AuthRequest.java",
    "content": "package cn.iocoder.springboot.lab67.nettyserverdemo.message.auth;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message;\n\n/**\n * 用户认证请求\n */\npublic class AuthRequest implements Message {\n\n    public static final String TYPE = \"AUTH_REQUEST\";\n\n    /**\n     * 认证 Token\n     */\n    private String accessToken;\n\n    public String getAccessToken() {\n        return accessToken;\n    }\n\n    public AuthRequest setAccessToken(String accessToken) {\n        this.accessToken = accessToken;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"AuthRequest{\" +\n                \"accessToken='\" + accessToken + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/message/auth/AuthResponse.java",
    "content": "package cn.iocoder.springboot.lab67.nettyserverdemo.message.auth;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message;\n\n/**\n * 用户认证响应\n */\npublic class AuthResponse implements Message {\n\n    public static final String TYPE = \"AUTH_RESPONSE\";\n\n    /**\n     * 响应状态码\n     */\n    private Integer code;\n    /**\n     * 响应提示\n     */\n    private String message;\n\n    public Integer getCode() {\n        return code;\n    }\n\n    public AuthResponse setCode(Integer code) {\n        this.code = code;\n        return this;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public AuthResponse setMessage(String message) {\n        this.message = message;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"AuthResponse{\" +\n                \"code=\" + code +\n                \", message='\" + message + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/message/chat/ChatRedirectToUserRequest.java",
    "content": "package cn.iocoder.springboot.lab67.nettyserverdemo.message.chat;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message;\n\n/**\n * 转发消息给一个用户的 Message\n */\npublic class ChatRedirectToUserRequest implements Message {\n\n    public static final String TYPE = \"CHAT_REDIRECT_TO_USER_REQUEST\";\n\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 内容\n     */\n    private String content;\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public ChatRedirectToUserRequest setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n    public String getContent() {\n        return content;\n    }\n\n    public ChatRedirectToUserRequest setContent(String content) {\n        this.content = content;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ChatRedirectToUserRequest{\" +\n                \"msgId='\" + msgId + '\\'' +\n                \", content='\" + content + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/message/chat/ChatSendResponse.java",
    "content": "package cn.iocoder.springboot.lab67.nettyserverdemo.message.chat;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message;\n\n/**\n * 聊天发送消息结果的 Response\n */\npublic class ChatSendResponse implements Message {\n\n    public static final String TYPE = \"CHAT_SEND_RESPONSE\";\n\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 响应状态码\n     */\n    private Integer code;\n    /**\n     * 响应提示\n     */\n    private String message;\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public ChatSendResponse setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n    public Integer getCode() {\n        return code;\n    }\n\n    public ChatSendResponse setCode(Integer code) {\n        this.code = code;\n        return this;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n    public ChatSendResponse setMessage(String message) {\n        this.message = message;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ChatSendResponse{\" +\n                \"msgId='\" + msgId + '\\'' +\n                \", code=\" + code +\n                \", message='\" + message + '\\'' +\n                '}';\n    }\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/message/chat/ChatSendToAllRequest.java",
    "content": "package cn.iocoder.springboot.lab67.nettyserverdemo.message.chat;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message;\n\n/**\n * 发送给所有人的群聊消息的 Message\n */\npublic class ChatSendToAllRequest implements Message {\n\n    public static final String TYPE = \"CHAT_SEND_TO_ALL_REQUEST\";\n\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 内容\n     */\n    private String content;\n\n    public String getContent() {\n        return content;\n    }\n\n    public ChatSendToAllRequest setContent(String content) {\n        this.content = content;\n        return this;\n    }\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public ChatSendToAllRequest setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ChatSendToAllRequest{\" +\n                \"msgId='\" + msgId + '\\'' +\n                \", content='\" + content + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/message/chat/ChatSendToOneRequest.java",
    "content": "package cn.iocoder.springboot.lab67.nettyserverdemo.message.chat;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message;\n\n/**\n * 发送给指定人的私聊消息 Request\n */\npublic class ChatSendToOneRequest implements Message {\n\n    public static final String TYPE = \"CHAT_SEND_TO_ONE_REQUEST\";\n\n    /**\n     * 发送给的用户\n     */\n    private String toUser;\n    /**\n     * 消息编号\n     */\n    private String msgId;\n    /**\n     * 内容\n     */\n    private String content;\n\n    public String getToUser() {\n        return toUser;\n    }\n\n    public ChatSendToOneRequest setToUser(String toUser) {\n        this.toUser = toUser;\n        return this;\n    }\n\n    public String getMsgId() {\n        return msgId;\n    }\n\n    public ChatSendToOneRequest setMsgId(String msgId) {\n        this.msgId = msgId;\n        return this;\n    }\n\n    public String getContent() {\n        return content;\n    }\n\n    public ChatSendToOneRequest setContent(String content) {\n        this.content = content;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"ChatSendToOneRequest{\" +\n                \"toUser='\" + toUser + '\\'' +\n                \", msgId='\" + msgId + '\\'' +\n                \", content='\" + content + '\\'' +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/message/heartbeat/HeartbeatRequest.java",
    "content": "package cn.iocoder.springboot.lab67.nettyserverdemo.message.heartbeat;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message;\n\n/**\n * 消息 - 心跳请求\n */\npublic class HeartbeatRequest implements Message {\n\n    /**\n     * 类型 - 心跳请求\n     */\n    public static final String TYPE = \"HEARTBEAT_REQUEST\";\n\n    @Override\n    public String toString() {\n        return \"HeartbeatRequest{}\";\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/message/heartbeat/HeartbeatResponse.java",
    "content": "package cn.iocoder.springboot.lab67.nettyserverdemo.message.heartbeat;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.Message;\n\n/**\n * 消息 - 心跳响应\n */\npublic class HeartbeatResponse implements Message {\n\n    /**\n     * 类型 - 心跳响应\n     */\n    public static final String TYPE = \"HEARTBEAT_RESPONSE\";\n\n    @Override\n    public String toString() {\n        return \"HeartbeatResponse{}\";\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/messagehandler/auth/AuthRequestHandler.java",
    "content": "package cn.iocoder.springboot.lab67.nettyserverdemo.messagehandler.auth;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.codec.Invocation;\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandler;\nimport cn.iocoder.springboot.lab67.nettyserverdemo.message.auth.AuthRequest;\nimport cn.iocoder.springboot.lab67.nettyserverdemo.message.auth.AuthResponse;\nimport cn.iocoder.springboot.lab67.nettyserverdemo.server.NettyChannelManager;\nimport io.netty.channel.Channel;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.StringUtils;\n\n@Component\npublic class AuthRequestHandler implements MessageHandler<AuthRequest> {\n\n    @Autowired\n    private NettyChannelManager nettyChannelManager;\n\n    @Override\n    public void execute(Channel channel, AuthRequest authRequest) {\n        // 如果未传递 accessToken\n        if (StringUtils.isEmpty(authRequest.getAccessToken())) {\n            AuthResponse authResponse = new AuthResponse().setCode(1).setMessage(\"认证 accessToken 未传入\");\n            channel.writeAndFlush(new Invocation(AuthResponse.TYPE, authResponse));\n            return;\n        }\n\n        // ... 此处应有一段\n\n        // 将用户和 Channel 绑定\n        // 考虑到代码简化，我们先直接使用 accessToken 作为 User\n        nettyChannelManager.addUser(channel, authRequest.getAccessToken());\n\n        // 响应认证成功\n        AuthResponse authResponse = new AuthResponse().setCode(0);\n        channel.writeAndFlush(new Invocation(AuthResponse.TYPE, authResponse));\n    }\n\n    @Override\n    public String getType() {\n        return AuthRequest.TYPE;\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/messagehandler/chat/ChatSendToAllHandler.java",
    "content": "package cn.iocoder.springboot.lab67.nettyserverdemo.messagehandler.chat;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.codec.Invocation;\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandler;\nimport cn.iocoder.springboot.lab67.nettyserverdemo.message.chat.ChatSendResponse;\nimport cn.iocoder.springboot.lab67.nettyserverdemo.message.chat.ChatSendToAllRequest;\nimport cn.iocoder.springboot.lab67.nettyserverdemo.message.chat.ChatRedirectToUserRequest;\nimport cn.iocoder.springboot.lab67.nettyserverdemo.server.NettyChannelManager;\nimport io.netty.channel.Channel;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ChatSendToAllHandler implements MessageHandler<ChatSendToAllRequest> {\n\n    @Autowired\n    private NettyChannelManager nettyChannelManager;\n\n    @Override\n    public void execute(Channel channel, ChatSendToAllRequest message) {\n        // 这里，假装直接成功\n        ChatSendResponse sendResponse = new ChatSendResponse().setMsgId(message.getMsgId()).setCode(0);\n        channel.writeAndFlush(new Invocation(ChatSendResponse.TYPE, sendResponse));\n\n        // 创建转发的消息，并广播发送\n        ChatRedirectToUserRequest sendToUserRequest = new ChatRedirectToUserRequest().setMsgId(message.getMsgId())\n                .setContent(message.getContent());\n        nettyChannelManager.sendAll(new Invocation(ChatRedirectToUserRequest.TYPE, sendToUserRequest));\n    }\n\n    @Override\n    public String getType() {\n        return ChatSendToAllRequest.TYPE;\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/messagehandler/chat/ChatSendToOneHandler.java",
    "content": "package cn.iocoder.springboot.lab67.nettyserverdemo.messagehandler.chat;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.codec.Invocation;\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandler;\nimport cn.iocoder.springboot.lab67.nettyserverdemo.message.chat.ChatSendResponse;\nimport cn.iocoder.springboot.lab67.nettyserverdemo.message.chat.ChatSendToOneRequest;\nimport cn.iocoder.springboot.lab67.nettyserverdemo.message.chat.ChatRedirectToUserRequest;\nimport cn.iocoder.springboot.lab67.nettyserverdemo.server.NettyChannelManager;\nimport io.netty.channel.Channel;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ChatSendToOneHandler implements MessageHandler<ChatSendToOneRequest> {\n\n    @Autowired\n    private NettyChannelManager nettyChannelManager;\n\n    @Override\n    public void execute(Channel channel, ChatSendToOneRequest message) {\n        // 这里，假装直接成功\n        ChatSendResponse sendResponse = new ChatSendResponse().setMsgId(message.getMsgId()).setCode(0);\n        channel.writeAndFlush(new Invocation(ChatSendResponse.TYPE, sendResponse));\n\n        // 创建转发的消息，发送给指定用户\n        ChatRedirectToUserRequest sendToUserRequest = new ChatRedirectToUserRequest().setMsgId(message.getMsgId())\n                .setContent(message.getContent());\n        nettyChannelManager.send(message.getToUser(), new Invocation(ChatRedirectToUserRequest.TYPE, sendToUserRequest));\n    }\n\n    @Override\n    public String getType() {\n        return ChatSendToOneRequest.TYPE;\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/messagehandler/heartbeat/HeartbeatRequestHandler.java",
    "content": "package cn.iocoder.springboot.lab67.nettyserverdemo.messagehandler.heartbeat;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.codec.Invocation;\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageHandler;\nimport cn.iocoder.springboot.lab67.nettyserverdemo.message.heartbeat.HeartbeatRequest;\nimport cn.iocoder.springboot.lab67.nettyserverdemo.message.heartbeat.HeartbeatResponse;\nimport io.netty.channel.Channel;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class HeartbeatRequestHandler implements MessageHandler<HeartbeatRequest> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void execute(Channel channel, HeartbeatRequest message) {\n        logger.info(\"[execute][收到连接({}) 的心跳请求]\", channel.id());\n        // 响应心跳\n        HeartbeatResponse response = new HeartbeatResponse();\n        channel.writeAndFlush(new Invocation(HeartbeatResponse.TYPE, response));\n    }\n\n    @Override\n    public String getType() {\n        return HeartbeatRequest.TYPE;\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/server/NettyChannelManager.java",
    "content": "package cn.iocoder.springboot.lab67.nettyserverdemo.server;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.codec.Invocation;\nimport io.netty.channel.Channel;\nimport io.netty.channel.ChannelId;\nimport io.netty.util.AttributeKey;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\n/**\n * 客户端 Channel 管理器。提供两种功能：\n * 1. 客户端 Channel 的管理\n * 2. 向客户端 Channel 发送消息\n */\n@Component\npublic class NettyChannelManager {\n\n    /**\n     * {@link Channel#attr(AttributeKey)} 属性中，表示 Channel 对应的用户\n     */\n    private static final AttributeKey<String> CHANNEL_ATTR_KEY_USER = AttributeKey.newInstance(\"user\");\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    /**\n     * Channel 映射\n     */\n    private ConcurrentMap<ChannelId, Channel> channels = new ConcurrentHashMap<>();\n    /**\n     * 用户与 Channel 的映射。\n     *\n     * 通过它，可以获取用户对应的 Channel。这样，我们可以向指定用户发送消息。\n     */\n    private ConcurrentMap<String, Channel> userChannels = new ConcurrentHashMap<>();\n\n    /**\n     * 添加 Channel 到 {@link #channels} 中\n     *\n     * @param channel Channel\n     */\n    public void add(Channel channel) {\n        channels.put(channel.id(), channel);\n        logger.info(\"[add][一个连接({})加入]\", channel.id());\n    }\n\n    /**\n     * 添加指定用户到 {@link #userChannels} 中\n     *\n     * @param channel Channel\n     * @param user 用户\n     */\n    public void addUser(Channel channel, String user) {\n        Channel existChannel = channels.get(channel.id());\n        if (existChannel == null) {\n            logger.error(\"[addUser][连接({}) 不存在]\", channel.id());\n            return;\n        }\n        // 设置属性\n        channel.attr(CHANNEL_ATTR_KEY_USER).set(user);\n        // 添加到 userChannels\n        userChannels.put(user, channel);\n    }\n\n    /**\n     * 将 Channel 从 {@link #channels} 和 {@link #userChannels} 中移除\n     *\n     * @param channel Channel\n     */\n    public void remove(Channel channel) {\n        // 移除 channels\n        channels.remove(channel.id());\n        // 移除 userChannels\n        if (channel.hasAttr(CHANNEL_ATTR_KEY_USER)) {\n            userChannels.remove(channel.attr(CHANNEL_ATTR_KEY_USER).get());\n        }\n        logger.info(\"[remove][一个连接({})离开]\", channel.id());\n    }\n\n    /**\n     * 向指定用户发送消息\n     *\n     * @param user 用户\n     * @param invocation 消息体\n     */\n    public void send(String user, Invocation invocation) {\n        // 获得用户对应的 Channel\n        Channel channel = userChannels.get(user);\n        if (channel == null) {\n            logger.error(\"[send][连接不存在]\");\n            return;\n        }\n        if (!channel.isActive()) {\n            logger.error(\"[send][连接({})未激活]\", channel.id());\n            return;\n        }\n        // 发送消息\n        channel.writeAndFlush(invocation);\n    }\n\n    /**\n     * 向所有用户发送消息\n     *\n     * @param invocation 消息体\n     */\n    public void sendAll(Invocation invocation) {\n        for (Channel channel : channels.values()) {\n            if (!channel.isActive()) {\n                logger.error(\"[send][连接({})未激活]\", channel.id());\n                return;\n            }\n            // 发送消息\n            channel.writeAndFlush(invocation);\n        }\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/server/NettyServer.java",
    "content": "package cn.iocoder.springboot.lab67.nettyserverdemo.server;\n\nimport cn.iocoder.springboot.lab67.nettyserverdemo.server.handler.NettyServerHandlerInitializer;\nimport io.netty.bootstrap.ServerBootstrap;\nimport io.netty.channel.Channel;\nimport io.netty.channel.ChannelFuture;\nimport io.netty.channel.ChannelOption;\nimport io.netty.channel.EventLoopGroup;\nimport io.netty.channel.nio.NioEventLoopGroup;\nimport io.netty.channel.socket.nio.NioServerSocketChannel;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.PostConstruct;\nimport javax.annotation.PreDestroy;\nimport java.net.InetSocketAddress;\n\n@Component\npublic class NettyServer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Value(\"${netty.port}\")\n    private Integer port;\n\n    @Autowired\n    private NettyServerHandlerInitializer nettyServerHandlerInitializer;\n\n    /**\n     * boss 线程组，用于服务端接受客户端的连接\n     */\n    private EventLoopGroup bossGroup = new NioEventLoopGroup();\n    /**\n     * worker 线程组，用于服务端接受客户端的数据读写\n     */\n    private EventLoopGroup workerGroup = new NioEventLoopGroup();\n    /**\n     * Netty Server Channel\n     */\n    private Channel channel;\n\n    /**\n     * 启动 Netty Server\n     */\n    @PostConstruct\n    public void start() throws InterruptedException {\n        // 创建 ServerBootstrap 对象，用于 Netty Server 启动\n        ServerBootstrap bootstrap = new ServerBootstrap();\n        // 设置 ServerBootstrap 的各种属性\n        bootstrap.group(bossGroup, workerGroup) // 设置两个 EventLoopGroup 对象\n                .channel(NioServerSocketChannel.class)  // 指定 Channel 为服务端 NioServerSocketChannel\n                .localAddress(new InetSocketAddress(port)) // 设置 Netty Server 的端口\n                .option(ChannelOption.SO_BACKLOG, 1024) // 服务端 accept 队列的大小\n                .childOption(ChannelOption.SO_KEEPALIVE, true) // TCP Keepalive 机制，实现 TCP 层级的心跳保活功能\n                .childOption(ChannelOption.TCP_NODELAY, true) // 允许较小的数据包的发送，降低延迟\n                .childHandler(nettyServerHandlerInitializer);\n        // 绑定端口，并同步等待成功，即启动服务端\n        ChannelFuture future = bootstrap.bind().sync();\n        if (future.isSuccess()) {\n            channel = future.channel();\n            logger.info(\"[start][Netty Server 启动在 {} 端口]\", port);\n        }\n    }\n\n    /**\n     * 关闭 Netty Server\n     */\n    @PreDestroy\n    public void shutdown() {\n        // 关闭 Netty Server\n        if (channel != null) {\n            channel.close();\n        }\n        // 优雅关闭两个 EventLoopGroup 对象\n        bossGroup.shutdownGracefully();\n        workerGroup.shutdownGracefully();\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/server/handler/NettyServerHandler.java",
    "content": "package cn.iocoder.springboot.lab67.nettyserverdemo.server.handler;\n\nimport cn.iocoder.springboot.lab67.nettyserverdemo.server.NettyChannelManager;\nimport io.netty.channel.ChannelHandler;\nimport io.netty.channel.ChannelHandlerContext;\nimport io.netty.channel.ChannelInboundHandlerAdapter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n/**\n * 服务端 Channel 实现类，提供对客户端 Channel 建立连接、断开连接、异常时的处理\n */\n@Component\n@ChannelHandler.Sharable\npublic class NettyServerHandler extends ChannelInboundHandlerAdapter {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private NettyChannelManager channelManager;\n\n    @Override\n    public void channelActive(ChannelHandlerContext ctx) {\n        // 从管理器中添加\n        channelManager.add(ctx.channel());\n    }\n\n    @Override\n    public void channelUnregistered(ChannelHandlerContext ctx) {\n        // 从管理器中移除\n        channelManager.remove(ctx.channel());\n    }\n\n    @Override\n    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {\n        logger.error(\"[exceptionCaught][连接({}) 发生异常]\", ctx.channel().id(), cause);\n        // 断开连接\n        ctx.channel().close();\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/java/cn/iocoder/springboot/lab67/nettyserverdemo/server/handler/NettyServerHandlerInitializer.java",
    "content": "package cn.iocoder.springboot.lab67.nettyserverdemo.server.handler;\n\nimport cn.iocoder.springboot.lab67.nettycommondemo.codec.InvocationDecoder;\nimport cn.iocoder.springboot.lab67.nettycommondemo.codec.InvocationEncoder;\nimport cn.iocoder.springboot.lab67.nettycommondemo.dispatcher.MessageDispatcher;\nimport io.netty.channel.Channel;\nimport io.netty.channel.ChannelInitializer;\nimport io.netty.channel.ChannelPipeline;\nimport io.netty.handler.timeout.ReadTimeoutHandler;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\nimport java.util.concurrent.TimeUnit;\n\n@Component\npublic class NettyServerHandlerInitializer extends ChannelInitializer<Channel> {\n\n    /**\n     * 心跳超时时间\n     */\n    private static final Integer READ_TIMEOUT_SECONDS = 3 * 60;\n\n    @Autowired\n    private MessageDispatcher messageDispatcher;\n    @Autowired\n    private NettyServerHandler nettyServerHandler;\n\n    @Override\n    protected void initChannel(Channel ch) {\n        // 获得 Channel 对应的 ChannelPipeline\n        ChannelPipeline channelPipeline = ch.pipeline();\n        // 添加一堆 NettyServerHandler 到 ChannelPipeline 中\n        channelPipeline\n                // 空闲检测\n                .addLast(new ReadTimeoutHandler(READ_TIMEOUT_SECONDS, TimeUnit.SECONDS))\n                // 编码器\n                .addLast(new InvocationEncoder())\n                // 解码器\n                .addLast(new InvocationDecoder())\n                // 消息分发器\n                .addLast(messageDispatcher)\n                // 服务端处理器\n                .addLast(nettyServerHandler)\n        ;\n    }\n\n}\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/lab-67-netty-demo-server/src/main/resources/application.yml",
    "content": "netty:\n  port: 8888 # Netty Server 端口\n"
  },
  {
    "path": "lab-67/lab-67-netty-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-67</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-67-netty-demo</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-67-netty-demo-server</module>\n        <module>lab-67-netty-demo-client</module>\n        <module>lab-67-netty-demo-common</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-67/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-67</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-67-netty-demo</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-67/《芋道 Spring Boot Netty 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/Netty/?github>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-authorization-code-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-68-spring-security-oauth</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-68-demo01-authorization-code-server</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动配置 -->\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.boot</groupId>-->\n<!--            <artifactId>spring-boot-starter-security</artifactId>-->\n<!--        </dependency>-->\n\n        <!-- 实现对 Spring Security OAuth2 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth.boot</groupId>\n            <artifactId>spring-security-oauth2-autoconfigure</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-authorization-code-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/ResourceServerApplication.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ResourceServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ResourceServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-authorization-code-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuth2AuthorizationServerConfig.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.config;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;\n\n/**\n * 授权服务器配置\n */\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\n\n    // 用户认证\n    @Autowired\n    private AuthenticationManager authenticationManager;\n\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n        endpoints.authenticationManager(authenticationManager);\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"clientapp\").secret(\"112233\") // Client 账号、密码。\n                .authorizedGrantTypes(\"authorization_code\") // 授权码模式\n                .redirectUris(\"http://127.0.0.1:9090/callback\") // 配置回调地址，选填。\n                .scopes(\"read_userinfo\", \"read_contacts\") // 可授权的 Scope\n//                .and().withClient() // 可以继续配置新的 Client\n                ;\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-authorization-code-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuth2ResourceServerConfig.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.config;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;\n\n/**\n * 资源服务器配置\n */\n@Configuration\n@EnableResourceServer\npublic class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n        http.authorizeRequests()\n                .anyRequest().authenticated()\n                // 设置 /api/ 开头的 URL 需要保护\n                .and().requestMatchers().antMatchers(\"/api/**\");\n    }\n\n}\n\n// 实际，OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。\n// 主要考虑，简化 demo ，所以改成这样。\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-authorization-code-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/SecurityConfig.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\n\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Override\n    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)\n    public AuthenticationManager authenticationManagerBean() throws Exception {\n        return super.authenticationManagerBean();\n    }\n\n    @Bean\n    public static NoOpPasswordEncoder passwordEncoder() {\n        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();\n    }\n\n    @Override\n    protected void configure(AuthenticationManagerBuilder auth) throws Exception {\n        auth.\n                // 使用内存中的 InMemoryUserDetailsManager\n                inMemoryAuthentication()\n                // 不使用 PasswordEncoder 密码编码器\n                .passwordEncoder(passwordEncoder())\n                // 配置 yunai 用户\n                .withUser(\"yunai\").password(\"1024\").roles(\"USER\");\n    }\n\n//    @Override\n//    protected void configure(HttpSecurity http) throws Exception {\n//        http.authorizeRequests()\n//                // 对所有 URL 都进行认证\n//                .anyRequest()\n//                .authenticated();\n//    }\n\n//    @Override\n//    public void configure(HttpSecurity http) throws Exception {\n//        http.csrf()\n//                .disable()\n//                .authorizeRequests()\n//                .antMatchers(\"/oauth/**\", \"/login/**\", \"/logout/**\").permitAll()\n//                .anyRequest().authenticated()\n//                .and().formLogin().permitAll();\n//    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-authorization-code-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/ExampleController.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.controller;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 示例模块 Controller\n */\n@RestController\n@RequestMapping(\"/api/example\")\npublic class ExampleController {\n\n    @RequestMapping(\"/hello\")\n    public String hello() {\n        return \"world\";\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-client-credentials-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-68-spring-security-oauth</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-68-demo01-client-credentials-server</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动配置 -->\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.boot</groupId>-->\n<!--            <artifactId>spring-boot-starter-security</artifactId>-->\n<!--        </dependency>-->\n\n        <!-- 实现对 Spring Security OAuth2 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth.boot</groupId>\n            <artifactId>spring-security-oauth2-autoconfigure</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-client-credentials-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/ResourceServerApplication.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ResourceServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ResourceServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-client-credentials-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuth2AuthorizationServerConfig.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\n\n/**\n * 授权服务器配置\n */\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\n\n    @Bean\n    public static NoOpPasswordEncoder passwordEncoder() {\n        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"clientapp\").secret(\"112233\") // Client 账号、密码。\n                .authorizedGrantTypes(\"client_credentials\") // 客户端模式\n                .scopes(\"read_userinfo\", \"read_contacts\") // 可授权的 Scope\n//                .and().withClient() // 可以继续配置新的 Client\n                ;\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-client-credentials-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuth2ResourceServerConfig.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.config;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;\n\n/**\n * 资源服务器配置\n */\n@Configuration\n@EnableResourceServer\npublic class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n        http.authorizeRequests()\n                .anyRequest().authenticated()\n                // 设置 /api/ 开头的 URL 需要保护\n                .and().requestMatchers().antMatchers(\"/api/**\");\n    }\n\n}\n\n// 实际，OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。\n// 主要考虑，简化 demo ，所以改成这样。\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-client-credentials-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/ExampleController.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.controller;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 示例模块 Controller\n */\n@RestController\n@RequestMapping(\"/api/example\")\npublic class ExampleController {\n\n    @RequestMapping(\"/hello\")\n    public String hello() {\n        return \"world\";\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-implicit-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-68-spring-security-oauth</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-68-demo01-implicit-server</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动配置 -->\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.boot</groupId>-->\n<!--            <artifactId>spring-boot-starter-security</artifactId>-->\n<!--        </dependency>-->\n\n        <!-- 实现对 Spring Security OAuth2 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth.boot</groupId>\n            <artifactId>spring-security-oauth2-autoconfigure</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-implicit-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/ResourceServerApplication.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ResourceServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ResourceServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-implicit-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuth2AuthorizationServerConfig.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.config;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;\n\n/**\n * 授权服务器配置\n */\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\n\n    // 用户认证\n    @Autowired\n    private AuthenticationManager authenticationManager;\n\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n        endpoints.authenticationManager(authenticationManager);\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"clientapp\").secret(\"112233\") // Client 账号、密码。\n                .authorizedGrantTypes(\"implicit\") // 授权码模式\n                .redirectUris(\"http://127.0.0.1:9090/callback02\") // 配置回调地址，选填。\n                .scopes(\"read_userinfo\", \"read_contacts\") // 可授权的 Scope\n//                .and().withClient() // 可以继续配置新的 Client\n                ;\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-implicit-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuth2ResourceServerConfig.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.config;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;\n\n/**\n * 资源服务器配置\n */\n@Configuration\n@EnableResourceServer\npublic class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n        http.authorizeRequests()\n                .anyRequest().authenticated()\n                // 设置 /api/ 开头的 URL 需要保护\n                .and().requestMatchers().antMatchers(\"/api/**\");\n    }\n\n}\n\n// 实际，OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。\n// 主要考虑，简化 demo ，所以改成这样。\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-implicit-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/SecurityConfig.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\n\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Override\n    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)\n    public AuthenticationManager authenticationManagerBean() throws Exception {\n        return super.authenticationManagerBean();\n    }\n\n    @Bean\n    public static NoOpPasswordEncoder passwordEncoder() {\n        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();\n    }\n\n    @Override\n    protected void configure(AuthenticationManagerBuilder auth) throws Exception {\n        auth.\n                // 使用内存中的 InMemoryUserDetailsManager\n                inMemoryAuthentication()\n                // 不使用 PasswordEncoder 密码编码器\n                .passwordEncoder(passwordEncoder())\n                // 配置 yunai 用户\n                .withUser(\"yunai\").password(\"1024\").roles(\"USER\");\n    }\n\n//    @Override\n//    protected void configure(HttpSecurity http) throws Exception {\n//        http.authorizeRequests()\n//                // 对所有 URL 都进行认证\n//                .anyRequest()\n//                .authenticated();\n//    }\n\n//    @Override\n//    public void configure(HttpSecurity http) throws Exception {\n//        http.csrf()\n//                .disable()\n//                .authorizeRequests()\n//                .antMatchers(\"/oauth/**\", \"/login/**\", \"/logout/**\").permitAll()\n//                .anyRequest().authenticated()\n//                .and().formLogin().permitAll();\n//    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-implicit-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/ExampleController.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.controller;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 示例模块 Controller\n */\n@RestController\n@RequestMapping(\"/api/example\")\npublic class ExampleController {\n\n    @RequestMapping(\"/hello\")\n    public String hello() {\n        return \"world\";\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-resource-owner-password-credentials-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-68-spring-security-oauth</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-68-demo01-resource-owner-password-credentials-server</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动配置 -->\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.boot</groupId>-->\n<!--            <artifactId>spring-boot-starter-security</artifactId>-->\n<!--        </dependency>-->\n\n        <!-- 实现对 Spring Security OAuth2 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth.boot</groupId>\n            <artifactId>spring-security-oauth2-autoconfigure</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-resource-owner-password-credentials-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/ResourceServerApplication.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ResourceServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ResourceServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-resource-owner-password-credentials-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuth2AuthorizationServerConfig.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.config;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;\n\n/**\n * 授权服务器配置\n */\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\n\n    // 用户认证\n    @Autowired\n    private AuthenticationManager authenticationManager;\n\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n        endpoints.authenticationManager(authenticationManager);\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"clientapp\").secret(\"112233\") // Client 账号、密码。\n                .authorizedGrantTypes(\"password\") // 密码模式\n                .scopes(\"read_userinfo\", \"read_contacts\") // 可授权的 Scope\n//                .and().withClient() // 可以继续配置新的 Client\n                ;\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-resource-owner-password-credentials-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuth2ResourceServerConfig.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.config;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;\n\n/**\n * 资源服务器配置\n */\n@Configuration\n@EnableResourceServer\npublic class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n        http.authorizeRequests()\n                .anyRequest().authenticated()\n                // 设置 /api/ 开头的 URL 需要保护\n                .and().requestMatchers().antMatchers(\"/api/**\");\n    }\n\n}\n\n// 实际，OAuth2ResourceServer 不是和 OAuth2AuthorizationServer 一起。\n// 主要考虑，简化 demo ，所以改成这样。\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-resource-owner-password-credentials-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/SecurityConfig.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\n\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Override\n    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)\n    public AuthenticationManager authenticationManagerBean() throws Exception {\n        return super.authenticationManagerBean();\n    }\n\n    @Bean\n    public static NoOpPasswordEncoder passwordEncoder() {\n        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();\n    }\n\n    @Override\n    protected void configure(AuthenticationManagerBuilder auth) throws Exception {\n        auth.\n                // 使用内存中的 InMemoryUserDetailsManager\n                inMemoryAuthentication()\n                // 不使用 PasswordEncoder 密码编码器\n                .passwordEncoder(passwordEncoder())\n                // 配置 yunai 用户\n                .withUser(\"yunai\").password(\"1024\").roles(\"USER\");\n    }\n\n//    @Override\n//    protected void configure(HttpSecurity http) throws Exception {\n//        http.authorizeRequests()\n//                // 对所有 URL 都进行认证\n//                .anyRequest()\n//                .authenticated();\n//    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo01-resource-owner-password-credentials-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/ExampleController.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.controller;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 示例模块 Controller\n */\n@RestController\n@RequestMapping(\"/api/example\")\npublic class ExampleController {\n\n    @RequestMapping(\"/hello\")\n    public String hello() {\n        return \"world\";\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-authorization-code/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-68-spring-security-oauth</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-68-demo02-authorization-server-with-authorization-code</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动配置 -->\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.boot</groupId>-->\n<!--            <artifactId>spring-boot-starter-security</artifactId>-->\n<!--        </dependency>-->\n\n        <!-- 实现对 Spring Security OAuth2 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth.boot</groupId>\n            <artifactId>spring-security-oauth2-autoconfigure</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-authorization-code/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/AuthorizationServerApplication.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class AuthorizationServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AuthorizationServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-authorization-code/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/OAuth2AuthorizationServerConfig.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo.config;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;\n\n/**\n * 授权服务器配置\n */\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\n\n    /**\n     * 用户认证 Manager\n     */\n    @Autowired\n    private AuthenticationManager authenticationManager;\n\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n        endpoints.authenticationManager(authenticationManager);\n    }\n\n    @Override\n    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {\n        oauthServer.checkTokenAccess(\"isAuthenticated()\")\n//            .tokenKeyAccess(\"permitAll()\")\n        ;\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"clientapp\").secret(\"112233\") // Client 账号、密码。\n                .authorizedGrantTypes(\"authorization_code\") // 授权码模式\n                .redirectUris(\"http://127.0.0.1:9090/callback\") // 配置回调地址，选填。\n                .scopes(\"read_userinfo\", \"read_contacts\") // 可授权的 Scope\n//                .and().withClient() // 可以继续配置新的 Client\n                ;\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-authorization-code/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/SecurityConfig.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\n\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Override\n    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)\n    public AuthenticationManager authenticationManagerBean() throws Exception {\n        return super.authenticationManagerBean();\n    }\n\n    @Bean\n    public static NoOpPasswordEncoder passwordEncoder() {\n        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();\n    }\n\n    @Override\n    protected void configure(AuthenticationManagerBuilder auth) throws Exception {\n        auth.\n                // 使用内存中的 InMemoryUserDetailsManager\n                inMemoryAuthentication()\n                // 不使用 PasswordEncoder 密码编码器\n                .passwordEncoder(passwordEncoder())\n                // 配置 yunai 用户\n                .withUser(\"yunai\").password(\"1024\").roles(\"USER\");\n    }\n\n//    @Override\n//    protected void configure(HttpSecurity http) throws Exception {\n//        http\n//                .authorizeRequests()\n//                .antMatchers(\"/oauth/**\").permitAll() // 允许无权限访问\n//                .anyRequest().authenticated()\n//                .and()\n//                .formLogin().and()\n//                .httpBasic();\n//    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-client-credentials/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-68-spring-security-oauth</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-68-demo02-authorization-server-with-client-credentials</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动配置 -->\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.boot</groupId>-->\n<!--            <artifactId>spring-boot-starter-security</artifactId>-->\n<!--        </dependency>-->\n\n        <!-- 实现对 Spring Security OAuth2 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth.boot</groupId>\n            <artifactId>spring-security-oauth2-autoconfigure</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-client-credentials/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/AuthorizationServerApplication.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class AuthorizationServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AuthorizationServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-client-credentials/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/OAuth2AuthorizationServerConfig.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;\n\n/**\n * 授权服务器配置\n */\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\n\n    /**\n     * 创建 PasswordEncoder Bean\n     */\n    @Bean\n    public static NoOpPasswordEncoder passwordEncoder() {\n        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();\n    }\n\n    @Override\n    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {\n        oauthServer.checkTokenAccess(\"isAuthenticated()\");\n//        oauthServer.tokenKeyAccess(\"isAuthenticated()\")\n//                .checkTokenAccess(\"isAuthenticated()\");\n//        oauthServer.tokenKeyAccess(\"permitAll()\")\n//                .checkTokenAccess(\"permitAll()\");\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"clientapp\").secret(\"112233\") // Client 账号、密码。\n                .authorizedGrantTypes(\"client_credentials\") // 客户端模式\n                .scopes(\"read_userinfo\", \"read_contacts\") // 可授权的 Scope\n//                .and().withClient() // 可以继续配置新的 Client\n                ;\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-implicit/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-68-spring-security-oauth</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-68-demo02-authorization-server-with-implicit</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动配置 -->\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.boot</groupId>-->\n<!--            <artifactId>spring-boot-starter-security</artifactId>-->\n<!--        </dependency>-->\n\n        <!-- 实现对 Spring Security OAuth2 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth.boot</groupId>\n            <artifactId>spring-security-oauth2-autoconfigure</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-implicit/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/AuthorizationServerApplication.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class AuthorizationServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AuthorizationServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-implicit/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/OAuth2AuthorizationServerConfig.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo.config;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;\n\n/**\n * 授权服务器配置\n */\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\n\n    /**\n     * 用户认证 Manager\n     */\n    @Autowired\n    private AuthenticationManager authenticationManager;\n\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n        endpoints.authenticationManager(authenticationManager);\n    }\n\n    @Override\n    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {\n        oauthServer.checkTokenAccess(\"isAuthenticated()\")\n//            .tokenKeyAccess(\"permitAll()\")\n        ;\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"clientapp\").secret(\"112233\") // Client 账号、密码。\n                .authorizedGrantTypes(\"implicit\") // 简化模式\n                .redirectUris(\"http://127.0.0.1:9090/callback02\") // 配置回调地址，选填。\n                .scopes(\"read_userinfo\", \"read_contacts\") // 可授权的 Scope\n//                .and().withClient() // 可以继续配置新的 Client\n                ;\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-implicit/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/SecurityConfig.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\n\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Override\n    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)\n    public AuthenticationManager authenticationManagerBean() throws Exception {\n        return super.authenticationManagerBean();\n    }\n\n    @Bean\n    public static NoOpPasswordEncoder passwordEncoder() {\n        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();\n    }\n\n    @Override\n    protected void configure(AuthenticationManagerBuilder auth) throws Exception {\n        auth.\n                // 使用内存中的 InMemoryUserDetailsManager\n                inMemoryAuthentication()\n                // 不使用 PasswordEncoder 密码编码器\n                .passwordEncoder(passwordEncoder())\n                // 配置 yunai 用户\n                .withUser(\"yunai\").password(\"1024\").roles(\"USER\");\n    }\n\n//    @Override\n//    protected void configure(HttpSecurity http) throws Exception {\n//        http\n//                .authorizeRequests()\n//                .antMatchers(\"/oauth/**\").permitAll() // 允许无权限访问\n//                .anyRequest().authenticated()\n//                .and()\n//                .formLogin().and()\n//                .httpBasic();\n//    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-resource-owner-password-credentials/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-68-spring-security-oauth</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-68-demo02-authorization-server-with-resource-owner-password-credentials</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动配置 -->\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.boot</groupId>-->\n<!--            <artifactId>spring-boot-starter-security</artifactId>-->\n<!--        </dependency>-->\n\n        <!-- 实现对 Spring Security OAuth2 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth.boot</groupId>\n            <artifactId>spring-security-oauth2-autoconfigure</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-resource-owner-password-credentials/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/AuthorizationServerApplication.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class AuthorizationServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AuthorizationServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-resource-owner-password-credentials/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/OAuth2AuthorizationServerConfig.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo.config;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;\n\n/**\n * 授权服务器配置\n */\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\n\n    /**\n     * 用户认证 Manager\n     */\n    @Autowired\n    private AuthenticationManager authenticationManager;\n\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n        endpoints.authenticationManager(authenticationManager);\n    }\n\n    @Override\n    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {\n        oauthServer.checkTokenAccess(\"isAuthenticated()\");\n//        oauthServer.tokenKeyAccess(\"isAuthenticated()\")\n//                .checkTokenAccess(\"isAuthenticated()\");\n//        oauthServer.tokenKeyAccess(\"permitAll()\")\n//                .checkTokenAccess(\"permitAll()\");\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"clientapp\").secret(\"112233\") // Client 账号、密码。\n                .authorizedGrantTypes(\"password\") // 密码模式\n                .scopes(\"read_userinfo\", \"read_contacts\") // 可授权的 Scope\n//                .and().withClient() // 可以继续配置新的 Client\n                ;\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-authorization-server-with-resource-owner-password-credentials/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/SecurityConfig.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\n\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Override\n    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)\n    public AuthenticationManager authenticationManagerBean() throws Exception {\n        return super.authenticationManagerBean();\n    }\n\n    @Bean\n    public static NoOpPasswordEncoder passwordEncoder() {\n        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();\n    }\n\n    @Override\n    protected void configure(AuthenticationManagerBuilder auth) throws Exception {\n        auth.\n                // 使用内存中的 InMemoryUserDetailsManager\n                inMemoryAuthentication()\n                // 不使用 PasswordEncoder 密码编码器\n                .passwordEncoder(passwordEncoder())\n                // 配置 yunai 用户\n                .withUser(\"yunai\").password(\"1024\").roles(\"USER\");\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-resource-server/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-68-spring-security-oauth</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-68-demo02-resource-server</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动配置 -->\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.boot</groupId>-->\n<!--            <artifactId>spring-boot-starter-security</artifactId>-->\n<!--        </dependency>-->\n\n        <!-- 实现对 Spring Security OAuth2 的自动配置 -->\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.security.oauth</groupId>-->\n<!--            <artifactId>spring-security-oauth2</artifactId>-->\n<!--            <version>2.5.0.RELEASE</version>-->\n<!--        </dependency>-->\n\n        <!-- 实现对 Spring Security OAuth2 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth.boot</groupId>\n            <artifactId>spring-security-oauth2-autoconfigure</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-resource-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/ResourceServerApplication.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ResourceServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ResourceServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-resource-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuth2ResourceServerConfig.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.config;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;\n\n/**\n * 资源服务器配置\n */\n@Configuration\n@EnableResourceServer\npublic class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {\n\n    @Override\n    public void configure(HttpSecurity http) throws Exception {\n        http.authorizeRequests()\n            // 设置 /login 无需权限访问\n            .antMatchers(\"/login\").permitAll()\n            // 设置 /client-login 无需权限访问\n            .antMatchers(\"/client-login\").permitAll()\n            /// 设置 /callback 无需权限访问\n            .antMatchers(\"/callback\").permitAll()\n            // 设置 /callback02 无需权限访问\n            .antMatchers(\"/callback02\").permitAll()\n            // 设置其它请求，需要认证后访问\n            .anyRequest().authenticated()\n            ;\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-resource-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/Callback02Controller.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/\")\npublic class Callback02Controller {\n\n    @GetMapping(\"/callback02\")\n    public String login() {\n        return \"假装这里有一个页面\";\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-resource-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/CallbackController.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;\nimport org.springframework.security.oauth2.client.OAuth2RestTemplate;\nimport org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeAccessTokenProvider;\nimport org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails;\nimport org.springframework.security.oauth2.common.OAuth2AccessToken;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/\")\npublic class CallbackController {\n\n    @Autowired\n    private OAuth2ClientProperties oauth2ClientProperties;\n\n    @Value(\"${security.oauth2.access-token-uri}\")\n    private String accessTokenUri;\n\n    @GetMapping(\"/callback\")\n    public OAuth2AccessToken login(@RequestParam(\"code\") String code) {\n        // 创建 AuthorizationCodeResourceDetails 对象\n        AuthorizationCodeResourceDetails resourceDetails = new AuthorizationCodeResourceDetails();\n        resourceDetails.setAccessTokenUri(accessTokenUri);\n        resourceDetails.setClientId(oauth2ClientProperties.getClientId());\n        resourceDetails.setClientSecret(oauth2ClientProperties.getClientSecret());\n        // 创建 OAuth2RestTemplate 对象\n        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails);\n        restTemplate.getOAuth2ClientContext().getAccessTokenRequest().setAuthorizationCode(code); // 设置 code\n        restTemplate.getOAuth2ClientContext().getAccessTokenRequest().setPreservedState(\"http://127.0.0.1:9090/callback\"); // 通过这个方式，设置 redirect_uri 参数\n        restTemplate.setAccessTokenProvider(new AuthorizationCodeAccessTokenProvider());\n        // 获取访问令牌\n        return restTemplate.getAccessToken();\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-resource-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/ClientLoginController.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;\nimport org.springframework.security.oauth2.client.OAuth2RestTemplate;\nimport org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider;\nimport org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;\nimport org.springframework.security.oauth2.common.OAuth2AccessToken;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/\")\npublic class ClientLoginController {\n\n    @Autowired\n    private OAuth2ClientProperties oauth2ClientProperties;\n\n    @Value(\"${security.oauth2.access-token-uri}\")\n    private String accessTokenUri;\n\n    @PostMapping(\"/client-login\")\n    public OAuth2AccessToken login() {\n        // 创建 ClientCredentialsResourceDetails 对象\n        ClientCredentialsResourceDetails resourceDetails = new ClientCredentialsResourceDetails();\n        resourceDetails.setAccessTokenUri(accessTokenUri);\n        resourceDetails.setClientId(oauth2ClientProperties.getClientId());\n        resourceDetails.setClientSecret(oauth2ClientProperties.getClientSecret());\n        // 创建 OAuth2RestTemplate 对象\n        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails);\n        restTemplate.setAccessTokenProvider(new ClientCredentialsAccessTokenProvider());\n        // 获取访问令牌\n        return restTemplate.getAccessToken();\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-resource-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/ExampleController.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.controller;\n\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 示例模块 Controller\n */\n@RestController\n@RequestMapping(\"/api/example\")\npublic class ExampleController {\n\n    @RequestMapping(\"/hello\")\n    public String hello() {\n        return \"world\";\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-resource-server/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/LoginController.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.autoconfigure.security.oauth2.OAuth2ClientProperties;\nimport org.springframework.security.oauth2.client.OAuth2RestTemplate;\nimport org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordAccessTokenProvider;\nimport org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails;\nimport org.springframework.security.oauth2.common.OAuth2AccessToken;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/\")\npublic class LoginController {\n\n    @Autowired\n    private OAuth2ClientProperties oauth2ClientProperties;\n\n    @Value(\"${security.oauth2.access-token-uri}\")\n    private String accessTokenUri;\n\n    @PostMapping(\"/login\")\n    public OAuth2AccessToken login(@RequestParam(\"username\") String username,\n                                   @RequestParam(\"password\") String password) {\n        // 创建 ResourceOwnerPasswordResourceDetails 对象\n        ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();\n        resourceDetails.setAccessTokenUri(accessTokenUri);\n        resourceDetails.setClientId(oauth2ClientProperties.getClientId());\n        resourceDetails.setClientSecret(oauth2ClientProperties.getClientSecret());\n        resourceDetails.setUsername(username);\n        resourceDetails.setPassword(password);\n        // 创建 OAuth2RestTemplate 对象\n        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails);\n        restTemplate.setAccessTokenProvider(new ResourceOwnerPasswordAccessTokenProvider());\n        // 获取访问令牌\n        return restTemplate.getAccessToken();\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo02-resource-server/src/main/resources/application.yml",
    "content": "server:\n  port: 9090\n\nsecurity:\n  oauth2:\n    # OAuth2 Client 配置，对应 OAuth2ClientProperties 类\n    client:\n      client-id: clientapp\n      client-secret: 112233\n    # OAuth2 Resource 配置，对应 ResourceServerProperties 类\n    resource:\n      token-info-uri: http://127.0.0.1:8080/oauth/check_token # 获得 Token 信息的 URL\n    # 访问令牌获取 URL，自定义的\n    access-token-uri: http://127.0.0.1:8080/oauth/token\n\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo03-authorization-server-with-resource-owner-password-credentials/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-68-spring-security-oauth</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-68-demo03-authorization-server-with-resource-owner-password-credentials</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动配置 -->\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.boot</groupId>-->\n<!--            <artifactId>spring-boot-starter-security</artifactId>-->\n<!--        </dependency>-->\n\n        <!-- 实现对 Spring Security OAuth2 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth.boot</groupId>\n            <artifactId>spring-security-oauth2-autoconfigure</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo03-authorization-server-with-resource-owner-password-credentials/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/AuthorizationServerApplication.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class AuthorizationServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AuthorizationServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo03-authorization-server-with-resource-owner-password-credentials/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/OAuth2AuthorizationServerConfig.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo.config;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;\n\n/**\n * 授权服务器配置\n */\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\n\n    /**\n     * 用户认证 Manager\n     */\n    @Autowired\n    private AuthenticationManager authenticationManager;\n\n    /**\n     * 用户详情 Service\n     */\n    @Autowired\n    private UserDetailsService userDetailsService;\n\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n        endpoints.authenticationManager(authenticationManager)\n            .userDetailsService(userDetailsService)\n        ;\n    }\n\n    @Override\n    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {\n        oauthServer.checkTokenAccess(\"isAuthenticated()\");\n//        oauthServer.tokenKeyAccess(\"isAuthenticated()\")\n//                .checkTokenAccess(\"isAuthenticated()\");\n//        oauthServer.tokenKeyAccess(\"permitAll()\")\n//                .checkTokenAccess(\"permitAll()\");\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"clientapp\").secret(\"112233\") // Client 账号、密码。\n                .authorizedGrantTypes(\"password\", \"refresh_token\") // 密码模式\n                .scopes(\"read_userinfo\", \"read_contacts\") // 可授权的 Scope\n                .accessTokenValiditySeconds(3600) // 访问令牌的有效期为 3600 秒 = 2 小时\n                .refreshTokenValiditySeconds(864000) // 刷新令牌的有效期为 864000 秒 = 10 天\n//                .and().withClient() // 可以继续配置新的 Client\n                ;\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo03-authorization-server-with-resource-owner-password-credentials/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/SecurityConfig.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.core.userdetails.UserDetailsService;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\n\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Override\n    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)\n    public AuthenticationManager authenticationManagerBean() throws Exception {\n        return super.authenticationManagerBean();\n    }\n\n    @Override\n    @Bean(name = BeanIds.USER_DETAILS_SERVICE)\n    public UserDetailsService userDetailsServiceBean() throws Exception {\n        return super.userDetailsServiceBean();\n    }\n\n    @Bean\n    public static NoOpPasswordEncoder passwordEncoder() {\n        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();\n    }\n\n    @Override\n    protected void configure(AuthenticationManagerBuilder auth) throws Exception {\n        auth.\n                // 使用内存中的 InMemoryUserDetailsManager\n                inMemoryAuthentication()\n                // 不使用 PasswordEncoder 密码编码器\n                .passwordEncoder(passwordEncoder())\n                // 配置 yunai 用户\n                .withUser(\"yunai\").password(\"1024\").roles(\"USER\");\n    }\n\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n        http.csrf().disable()\n                .authorizeRequests()\n                // 设置 /token/demo/revoke 无需授权\n                .mvcMatchers(\"/token/demo/revoke\").permitAll()\n                // 设置其它接口需要授权\n                .anyRequest().authenticated();\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo03-authorization-server-with-resource-owner-password-credentials/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/controller/TokenDemoController.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.security.oauth2.provider.token.ConsumerTokenServices;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * Token 示例 Controller\n */\n@RestController\n@RequestMapping(\"/token/demo\")\npublic class TokenDemoController {\n\n    @Autowired\n    private ConsumerTokenServices tokenServices;\n\n    @PostMapping(value = \"/revoke\")\n    public boolean revokeToken(@RequestParam(\"token\") String token) {\n        return tokenServices.revokeToken(token);\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jdbc-store/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-68-spring-security-oauth</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-68-demo11-authorization-server-by-jdbc-store</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动配置 -->\n        <!--        <dependency>-->\n        <!--            <groupId>org.springframework.boot</groupId>-->\n        <!--            <artifactId>spring-boot-starter-security</artifactId>-->\n        <!--        </dependency>-->\n\n        <!-- 实现对 Spring Security OAuth2 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth.boot</groupId>\n            <artifactId>spring-security-oauth2-autoconfigure</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n    </dependencies>\n\n\n</project>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jdbc-store/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/AuthorizationServerApplication.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class AuthorizationServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AuthorizationServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jdbc-store/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/OAuth2AuthorizationServerConfig.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo.config;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;\nimport org.springframework.security.oauth2.provider.ClientDetailsService;\nimport org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;\nimport org.springframework.security.oauth2.provider.token.TokenStore;\nimport org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;\n\nimport javax.sql.DataSource;\n\n/**\n * 授权服务器配置\n */\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\n\n    /**\n     * 用户认证 Manager\n     */\n    @Autowired\n    private AuthenticationManager authenticationManager;\n\n    /**\n     * 数据源 DataSource\n     */\n    @Autowired\n    private DataSource dataSource;\n\n    @Bean\n    public TokenStore jdbcTokenStore() {\n        return new JdbcTokenStore(dataSource);\n    }\n\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n        endpoints.authenticationManager(authenticationManager)\n            .tokenStore(jdbcTokenStore());\n    }\n\n    @Override\n    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {\n        oauthServer.checkTokenAccess(\"isAuthenticated()\");\n//        oauthServer.tokenKeyAccess(\"isAuthenticated()\")\n//                .checkTokenAccess(\"isAuthenticated()\");\n//        oauthServer.tokenKeyAccess(\"permitAll()\")\n//                .checkTokenAccess(\"permitAll()\");\n    }\n\n    @Bean\n    public ClientDetailsService jdbcClientDetailsService() {\n        return new JdbcClientDetailsService(dataSource);\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.withClientDetails(jdbcClientDetailsService());\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jdbc-store/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/SecurityConfig.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\n\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Override\n    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)\n    public AuthenticationManager authenticationManagerBean() throws Exception {\n        return super.authenticationManagerBean();\n    }\n\n    @Bean\n    public static NoOpPasswordEncoder passwordEncoder() {\n        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();\n    }\n\n    @Override\n    protected void configure(AuthenticationManagerBuilder auth) throws Exception {\n        auth.\n                // 使用内存中的 InMemoryUserDetailsManager\n                inMemoryAuthentication()\n                // 不使用 PasswordEncoder 密码编码器\n                .passwordEncoder(passwordEncoder())\n                // 配置 yunai 用户\n                .withUser(\"yunai\").password(\"1024\").roles(\"USER\");\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jdbc-store/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容，对应 DataSourceProperties 配置属性类\n  datasource:\n    url: jdbc:mysql://127.0.0.1:43063/demo-68-authorization-server?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root # 数据库账号\n    password: 123456 # 数据库密码\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jdbc-store/src/main/resources/db/data.sql",
    "content": "INSERT INTO oauth_client_details\n\t(client_id, client_secret, scope, authorized_grant_types,\n\tweb_server_redirect_uri, authorities, access_token_validity,\n\trefresh_token_validity, additional_information, autoapprove)\nVALUES\n\t('clientapp', '112233', 'read_userinfo,read_contacts',\n\t'password,refresh_token', null, null, 3600, 864000, null, true);\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jdbc-store/src/main/resources/db/schema.sql",
    "content": "--------------- MySQL ---------------\ndrop table if exists oauth_client_details;\ncreate table oauth_client_details (\n  client_id VARCHAR(255) PRIMARY KEY,\n  resource_ids VARCHAR(255),\n  client_secret VARCHAR(255),\n  scope VARCHAR(255),\n  authorized_grant_types VARCHAR(255),\n  web_server_redirect_uri VARCHAR(255),\n  authorities VARCHAR(255),\n  access_token_validity INTEGER,\n  refresh_token_validity INTEGER,\n  additional_information VARCHAR(4096),\n  autoapprove VARCHAR(255)\n);\n\ncreate table if not exists oauth_client_token (\n  token_id VARCHAR(255),\n  token LONG VARBINARY,\n  authentication_id VARCHAR(255) PRIMARY KEY,\n  user_name VARCHAR(255),\n  client_id VARCHAR(255)\n);\n\ncreate table if not exists oauth_access_token (\n  token_id VARCHAR(255),\n  token LONG VARBINARY,\n  authentication_id VARCHAR(255) PRIMARY KEY,\n  user_name VARCHAR(255),\n  client_id VARCHAR(255),\n  authentication LONG VARBINARY,\n  refresh_token VARCHAR(255)\n);\n\ncreate table if not exists oauth_refresh_token (\n  token_id VARCHAR(255),\n  token LONG VARBINARY,\n  authentication LONG VARBINARY\n);\n\ncreate table if not exists oauth_code (\n  code VARCHAR(255), authentication LONG VARBINARY\n);\n\ncreate table if not exists oauth_approvals (\n\tuserId VARCHAR(255),\n\tclientId VARCHAR(255),\n\tscope VARCHAR(255),\n\tstatus VARCHAR(10),\n\texpiresAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n\tlastModifiedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jwt-store/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-68-spring-security-oauth</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-68-demo11-authorization-server-by-jwt-store</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动配置 -->\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.boot</groupId>-->\n<!--            <artifactId>spring-boot-starter-security</artifactId>-->\n<!--        </dependency>-->\n\n        <!-- 实现对 Spring Security OAuth2 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth.boot</groupId>\n            <artifactId>spring-security-oauth2-autoconfigure</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jwt-store/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/AuthorizationServerApplication.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class AuthorizationServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AuthorizationServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jwt-store/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/OAuth2AuthorizationServerConfig.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo.config;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;\nimport org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;\nimport org.springframework.security.oauth2.provider.token.store.JwtTokenStore;\n\n/**\n * 授权服务器配置\n */\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\n\n    /**\n     * 用户认证 Manager\n     */\n    @Autowired\n    private AuthenticationManager authenticationManager;\n\n    @Bean\n    public JwtAccessTokenConverter jwtAccessTokenConverter() {\n        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();\n        converter.setSigningKey(\"nainai_zui_shuai\"); // JWT 秘钥\n        return converter;\n    }\n\n    @Bean\n    public JwtTokenStore jwtTokenStore() {\n        return new JwtTokenStore(jwtAccessTokenConverter());\n    }\n\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n        endpoints.authenticationManager(authenticationManager)\n            .tokenStore(jwtTokenStore())\n            .accessTokenConverter(jwtAccessTokenConverter());\n    }\n\n    @Override\n    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {\n        oauthServer.checkTokenAccess(\"isAuthenticated()\");\n//        oauthServer.tokenKeyAccess(\"isAuthenticated()\")\n//                .checkTokenAccess(\"isAuthenticated()\");\n//        oauthServer.tokenKeyAccess(\"permitAll()\")\n//                .checkTokenAccess(\"permitAll()\");\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"clientapp\").secret(\"112233\") // Client 账号、密码。\n                .authorizedGrantTypes(\"password\", \"refresh_token\") // 密码模式\n                .scopes(\"read_userinfo\", \"read_contacts\") // 可授权的 Scope\n//                .and().withClient() // 可以继续配置新的 Client\n                ;\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-jwt-store/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/SecurityConfig.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\n\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Override\n    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)\n    public AuthenticationManager authenticationManagerBean() throws Exception {\n        return super.authenticationManagerBean();\n    }\n\n    @Bean\n    public static NoOpPasswordEncoder passwordEncoder() {\n        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();\n    }\n\n    @Override\n    protected void configure(AuthenticationManagerBuilder auth) throws Exception {\n        auth.\n                // 使用内存中的 InMemoryUserDetailsManager\n                inMemoryAuthentication()\n                // 不使用 PasswordEncoder 密码编码器\n                .passwordEncoder(passwordEncoder())\n                // 配置 yunai 用户\n                .withUser(\"yunai\").password(\"1024\").roles(\"USER\");\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-redis-store/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-68-spring-security-oauth</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-68-demo11-authorization-server-by-redis-store</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动配置 -->\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.boot</groupId>-->\n<!--            <artifactId>spring-boot-starter-security</artifactId>-->\n<!--        </dependency>-->\n\n        <!-- 实现对 Spring Security OAuth2 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth.boot</groupId>\n            <artifactId>spring-security-oauth2-autoconfigure</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n\n        <!-- 实现对 Spring Data Redis 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-redis-store/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/AuthorizationServerApplication.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class AuthorizationServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AuthorizationServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-redis-store/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/OAuth2AuthorizationServerConfig.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo.config;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;\nimport org.springframework.security.oauth2.provider.token.TokenStore;\nimport org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;\n\n/**\n * 授权服务器配置\n */\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\n\n    /**\n     * 用户认证 Manager\n     */\n    @Autowired\n    private AuthenticationManager authenticationManager;\n\n    /**\n     * Redis 连接的工厂\n     */\n    @Autowired\n    private RedisConnectionFactory redisConnectionFactory;\n\n    @Bean\n    public TokenStore redisTokenStore() {\n        return new RedisTokenStore(redisConnectionFactory);\n    }\n\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n        endpoints.authenticationManager(authenticationManager)\n            .tokenStore(redisTokenStore());\n    }\n\n    @Override\n    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {\n        oauthServer.checkTokenAccess(\"isAuthenticated()\");\n//        oauthServer.tokenKeyAccess(\"isAuthenticated()\")\n//                .checkTokenAccess(\"isAuthenticated()\");\n//        oauthServer.tokenKeyAccess(\"permitAll()\")\n//                .checkTokenAccess(\"permitAll()\");\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.inMemory()\n                .withClient(\"clientapp\").secret(\"112233\") // Client 账号、密码。\n                .authorizedGrantTypes(\"password\", \"refresh_token\") // 密码模式\n                .scopes(\"read_userinfo\", \"read_contacts\") // 可授权的 Scope\n//                .and().withClient() // 可以继续配置新的 Client\n                ;\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-redis-store/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/SecurityConfig.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\n\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Override\n    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)\n    public AuthenticationManager authenticationManagerBean() throws Exception {\n        return super.authenticationManagerBean();\n    }\n\n    @Bean\n    public static NoOpPasswordEncoder passwordEncoder() {\n        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();\n    }\n\n    @Override\n    protected void configure(AuthenticationManagerBuilder auth) throws Exception {\n        auth.\n                // 使用内存中的 InMemoryUserDetailsManager\n                inMemoryAuthentication()\n                // 不使用 PasswordEncoder 密码编码器\n                .passwordEncoder(passwordEncoder())\n                // 配置 yunai 用户\n                .withUser(\"yunai\").password(\"1024\").roles(\"USER\");\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo11-authorization-server-by-redis-store/src/main/resources/application.yml",
    "content": "spring:\n  # 对应 RedisProperties 类\n  redis:\n    host: 127.0.0.1\n    port: 6379\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo21-authorization-server-on-sso/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-68-spring-security-oauth</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-68-demo21-authorization-server-on-sso</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动配置 -->\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.boot</groupId>-->\n<!--            <artifactId>spring-boot-starter-security</artifactId>-->\n<!--        </dependency>-->\n\n        <!-- 实现对 Spring Security OAuth2 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth.boot</groupId>\n            <artifactId>spring-security-oauth2-autoconfigure</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo21-authorization-server-on-sso/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/AuthorizationServerApplication.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class AuthorizationServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AuthorizationServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo21-authorization-server-on-sso/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/OAuth2AuthorizationServerConfig.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo.config;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;\nimport org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;\nimport org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;\nimport org.springframework.security.oauth2.provider.ClientDetailsService;\nimport org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;\nimport org.springframework.security.oauth2.provider.token.TokenStore;\nimport org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;\n\nimport javax.sql.DataSource;\n\n/**\n * 授权服务器配置\n */\n@Configuration\n@EnableAuthorizationServer\npublic class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {\n\n    /**\n     * 用户认证 Manager\n     */\n    @Autowired\n    private AuthenticationManager authenticationManager;\n\n    /**\n     * 数据源 DataSource\n     */\n    @Autowired\n    private DataSource dataSource;\n\n    @Bean\n    public TokenStore jdbcTokenStore() {\n        return new JdbcTokenStore(dataSource);\n    }\n\n    @Override\n    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {\n        endpoints.authenticationManager(authenticationManager)\n                .tokenStore(jdbcTokenStore());\n    }\n\n    @Override\n    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {\n        oauthServer.checkTokenAccess(\"isAuthenticated()\");\n//        oauthServer.tokenKeyAccess(\"isAuthenticated()\")\n//                .checkTokenAccess(\"isAuthenticated()\");\n//        oauthServer.tokenKeyAccess(\"permitAll()\")\n//                .checkTokenAccess(\"permitAll()\");\n    }\n\n    @Bean\n    public ClientDetailsService jdbcClientDetailsService() {\n        return new JdbcClientDetailsService(dataSource);\n    }\n\n    @Override\n    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {\n        clients.withClientDetails(jdbcClientDetailsService());\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo21-authorization-server-on-sso/src/main/java/cn/iocoder/springboot/lab68/authorizationserverdemo/config/SecurityConfig.java",
    "content": "package cn.iocoder.springboot.lab68.authorizationserverdemo.config;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.authentication.AuthenticationManager;\nimport org.springframework.security.config.BeanIds;\nimport org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;\nimport org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\nimport org.springframework.security.crypto.password.NoOpPasswordEncoder;\n\nimport javax.sql.DataSource;\n\n@Configuration\n@EnableWebSecurity\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n\n    /**\n     * 数据源 DataSource\n     */\n    @Autowired\n    private DataSource dataSource;\n\n    @Override\n    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)\n    public AuthenticationManager authenticationManagerBean() throws Exception {\n        return super.authenticationManagerBean();\n    }\n\n    @Bean\n    public static NoOpPasswordEncoder passwordEncoder() {\n        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();\n    }\n\n    @Override\n    protected void configure(AuthenticationManagerBuilder auth) throws Exception {\n        auth.jdbcAuthentication()\n                .dataSource(dataSource);\n    }\n\n//    @Override\n//    protected void configure(HttpSecurity http) throws Exception {\n//        http\n//                .authorizeRequests()\n//                .antMatchers(\"/oauth/**\").permitAll() // 允许无权限访问\n//                .anyRequest().authenticated()\n//                .and()\n//                .formLogin().and()\n//                .httpBasic();\n//    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo21-authorization-server-on-sso/src/main/resources/application.yaml",
    "content": "spring:\n  # datasource 数据源配置内容，对应 DataSourceProperties 配置属性类\n  datasource:\n    url: jdbc:mysql://127.0.0.1:43063/demo-68-authorization-server-sso?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root # 数据库账号\n    password: 123456 # 数据库密码\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo21-authorization-server-on-sso/src/main/resources/db/oauth_data.sql",
    "content": "INSERT INTO oauth_client_details\n\t(client_id, client_secret, scope, authorized_grant_types,\n\tweb_server_redirect_uri, authorities, access_token_validity,\n\trefresh_token_validity, additional_information, autoapprove)\nVALUES\n\t('clientapp', '112233', 'read_userinfo,read_contacts',\n\t'password,authorization_code,refresh_token', 'http://127.0.0.1:9090/login', null, 3600, 864000, null, true);\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo21-authorization-server-on-sso/src/main/resources/db/oauth_schema.sql",
    "content": "drop table if exists oauth_client_details;\ncreate table oauth_client_details (\n  client_id VARCHAR(255) PRIMARY KEY,\n  resource_ids VARCHAR(255),\n  client_secret VARCHAR(255),\n  scope VARCHAR(255),\n  authorized_grant_types VARCHAR(255),\n  web_server_redirect_uri VARCHAR(255),\n  authorities VARCHAR(255),\n  access_token_validity INTEGER,\n  refresh_token_validity INTEGER,\n  additional_information VARCHAR(4096),\n  autoapprove VARCHAR(255)\n);\n\ncreate table if not exists oauth_client_token (\n  token_id VARCHAR(255),\n  token LONG VARBINARY,\n  authentication_id VARCHAR(255) PRIMARY KEY,\n  user_name VARCHAR(255),\n  client_id VARCHAR(255)\n);\n\ncreate table if not exists oauth_access_token (\n  token_id VARCHAR(255),\n  token LONG VARBINARY,\n  authentication_id VARCHAR(255) PRIMARY KEY,\n  user_name VARCHAR(255),\n  client_id VARCHAR(255),\n  authentication LONG VARBINARY,\n  refresh_token VARCHAR(255)\n);\n\ncreate table if not exists oauth_refresh_token (\n  token_id VARCHAR(255),\n  token LONG VARBINARY,\n  authentication LONG VARBINARY\n);\n\ncreate table if not exists oauth_code (\n  code VARCHAR(255), authentication LONG VARBINARY\n);\n\ncreate table if not exists oauth_approvals (\n\tuserId VARCHAR(255),\n\tclientId VARCHAR(255),\n\tscope VARCHAR(255),\n\tstatus VARCHAR(10),\n\texpiresAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n\tlastModifiedAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo21-authorization-server-on-sso/src/main/resources/db/user_data.sql",
    "content": "INSERT INTO `authorities` VALUES ('yunai', 'ROLE_USER');\n\nINSERT INTO `users` VALUES ('yunai', '112233', '1');\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo21-authorization-server-on-sso/src/main/resources/db/user_schema.sql",
    "content": "DROP TABLE IF EXISTS `authorities`;\nCREATE TABLE `authorities` (\n  `username` varchar(50) NOT NULL,\n  `authority` varchar(50) NOT NULL,\n  UNIQUE KEY `ix_auth_username` (`username`,`authority`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n\nDROP TABLE IF EXISTS `users`;\nCREATE TABLE `users` (\n  `username` varchar(50) NOT NULL,\n  `password` varchar(500) NOT NULL,\n  `enabled` tinyint(1) NOT NULL,\n  PRIMARY KEY (`username`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo21-resource-server-on-sso/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-68-spring-security-oauth</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-68-demo21-resource-server-on-sso</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动配置 -->\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.boot</groupId>-->\n<!--            <artifactId>spring-boot-starter-security</artifactId>-->\n<!--        </dependency>-->\n\n        <!-- 实现对 Spring Security OAuth2 的自动配置 -->\n<!--        <dependency>-->\n<!--            <groupId>org.springframework.security.oauth</groupId>-->\n<!--            <artifactId>spring-security-oauth2</artifactId>-->\n<!--            <version>2.5.0.RELEASE</version>-->\n<!--        </dependency>-->\n\n        <!-- 实现对 Spring Security OAuth2 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.security.oauth.boot</groupId>\n            <artifactId>spring-security-oauth2-autoconfigure</artifactId>\n            <version>${spring.boot.version}</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo21-resource-server-on-sso/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/ResourceServerApplication.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ResourceServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ResourceServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo21-resource-server-on-sso/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/OAuthSsoConfig.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.config;\n\nimport org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * SSO 配置\n *\n * 推荐看 SsoSecurityConfigurer 类\n */\n@Configuration\n@EnableOAuth2Sso // 开启 Sso 功能\npublic class OAuthSsoConfig {\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo21-resource-server-on-sso/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/config/SecurityConfig.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.config;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.core.annotation.Order;\nimport org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\n\n@Configuration\n@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启对 Spring Security 注解的方法，进行权限验证。\n@Order(101) // OAuth2SsoDefaultConfiguration 使用了 Order(100)，所以这里设置为 Order(101)，防止相同顺序导致报错\npublic class SecurityConfig extends WebSecurityConfigurerAdapter {\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo21-resource-server-on-sso/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.controller;\n\nimport org.springframework.security.access.prepost.PreAuthorize;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 示例 Controller\n */\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @GetMapping(\"/admin-list\")\n    @PreAuthorize(\"hasRole('ADMIN')\") // 要求管理员 ROLE_ADMIN 角色\n    public String adminList() {\n        return \"管理员列表\";\n    }\n\n    @GetMapping(\"/user-list\")\n    @PreAuthorize(\"hasRole('USER')\") // 要求普通用户 ROLE_USER 角色\n    public String userList() {\n        return \"用户列表\";\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo21-resource-server-on-sso/src/main/java/cn/iocoder/springboot/lab68/resourceserverdemo/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab68.resourceserverdemo.controller;\n\nimport org.springframework.security.core.Authentication;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n/**\n * 用户 Controller\n */\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @RequestMapping(\"/info\")\n    public Authentication info(Authentication authentication) {\n        return authentication;\n    }\n\n}\n"
  },
  {
    "path": "lab-68-spring-security-oauth/lab-68-demo21-resource-server-on-sso/src/main/resources/application.yml",
    "content": "server:\n  port: 9090\n  servlet:\n    session:\n      cookie:\n        name: SSO-SESSIONID # 自定义 Session 的 Cookie 名字，防止冲突。冲突后，会导致 SSO 登陆失败。\n\nsecurity:\n  oauth2:\n    # OAuth2 Client 配置，对应 OAuth2ClientProperties 类\n    client:\n      client-id: clientapp\n      client-secret: 112233\n      user-authorization-uri: http://127.0.0.1:8080/oauth/authorize # 获取用户的授权码地址\n      access-token-uri: http://127.0.0.1:8080/oauth/token # 获取访问令牌的地址\n    # OAuth2 Resource 配置，对应 ResourceServerProperties 类\n    resource:\n      token-info-uri: http://127.0.0.1:8080/oauth/check_token # 校验访问令牌是否有效的地址\n"
  },
  {
    "path": "lab-68-spring-security-oauth/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-68-spring-security-oauth</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-68-demo01-resource-owner-password-credentials-server</module>\n        <module>lab-68-demo01-authorization-code-server</module>\n        <module>lab-68-demo01-implicit-server</module>\n        <module>lab-68-demo01-client-credentials-server</module>\n\n        <module>lab-68-demo02-resource-server</module>\n        <module>lab-68-demo02-authorization-server-with-resource-owner-password-credentials</module>\n        <module>lab-68-demo02-authorization-server-with-authorization-code</module>\n        <module>lab-68-demo02-authorization-server-with-implicit</module>\n        <module>lab-68-demo02-authorization-server-with-client-credentials</module>\n\n        <module>lab-68-demo03-authorization-server-with-resource-owner-password-credentials</module>\n\n        <module>lab-68-demo11-authorization-server-by-jdbc-store</module>\n        <module>lab-68-demo11-authorization-server-by-redis-store</module>\n        <module>lab-68-demo11-authorization-server-by-jwt-store</module>\n\n        <module>lab-68-demo21-authorization-server-on-sso</module>\n        <module>lab-68-demo21-resource-server-on-sso</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/《芋道 Spring Security OAuth2 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Security/OAuth2-learning/?github>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/《芋道 Spring Security OAuth2 单点登陆》.md",
    "content": "<http://www.iocoder.cn/Spring-Security/OAuth2-learning-sso/?github>\n"
  },
  {
    "path": "lab-68-spring-security-oauth/《芋道 Spring Security OAuth2 存储器》.md",
    "content": "<http://www.iocoder.cn/Spring-Security/OAuth2-learning-store/?github>\n"
  },
  {
    "path": "lab-69-proxy/lab-69-proxy-cglib/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-69</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-69-proxy-cglib</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>cglib</groupId>\n            <artifactId>cglib</artifactId>\n            <version>3.3.0</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-69-proxy/lab-69-proxy-cglib/src/main/java/cn/iocoder/springboot/labs/lab69/TestProxyMain.java",
    "content": "package cn.iocoder.springboot.labs.lab69;\n\nimport cn.iocoder.springboot.labs.lab69.intercept.UserServiceMethodInterceptor;\nimport cn.iocoder.springboot.labs.lab69.service.UserServiceImpl;\nimport net.sf.cglib.proxy.Enhancer;\n\npublic class TestProxyMain {\n\n    public static void main(String[] args) {\n        // 创建 cglib 增强对象\n        Enhancer enhancer = new Enhancer();\n        enhancer.setSuperclass(UserServiceImpl.class); // 设置父类\n        enhancer.setCallback(new UserServiceMethodInterceptor());\n        // 创建代理\n        UserServiceImpl userService = (UserServiceImpl) enhancer.create();\n        userService.create(\"yunai\", \"buzhidao\");\n    }\n\n}\n"
  },
  {
    "path": "lab-69-proxy/lab-69-proxy-cglib/src/main/java/cn/iocoder/springboot/labs/lab69/intercept/UserServiceMethodInterceptor.java",
    "content": "package cn.iocoder.springboot.labs.lab69.intercept;\n\nimport net.sf.cglib.proxy.MethodInterceptor;\nimport net.sf.cglib.proxy.MethodProxy;\n\nimport java.lang.reflect.Method;\n\npublic class UserServiceMethodInterceptor implements MethodInterceptor {\n\n    public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {\n        System.out.println(\"before invoke\");\n        Object ret = methodProxy.invokeSuper(object, args);\n        System.out.println(\"after invoke\");\n        return ret;\n    }\n\n}\n"
  },
  {
    "path": "lab-69-proxy/lab-69-proxy-cglib/src/main/java/cn/iocoder/springboot/labs/lab69/service/UserServiceImpl.java",
    "content": "package cn.iocoder.springboot.labs.lab69.service;\n\npublic class UserServiceImpl {\n\n    public void create(String username, String password) {\n        System.out.println(String.format(\"登陆的用户名(%s) 密码(%s)\", username, password));\n    }\n\n}\n"
  },
  {
    "path": "lab-69-proxy/lab-69-proxy-jdk/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-69</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-69-proxy-jdk</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>commons-io</groupId>\n            <artifactId>commons-io</artifactId>\n            <version>2.7</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-69-proxy/lab-69-proxy-jdk/src/main/java/cn/iocoder/springboot/labs/lab69/GenerateProxyMain.java",
    "content": "package cn.iocoder.springboot.labs.lab69;\n\nimport cn.iocoder.springboot.labs.lab69.service.UserServiceImpl;\nimport org.apache.commons.io.IOUtils;\nimport sun.misc.ProxyGenerator;\n\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.lang.reflect.Proxy;\n\n/**\n * 生成 JDK {@link Proxy} 的示例代码\n *\n * 生成后，我们可以反编译查看具体的类\n */\npublic class GenerateProxyMain {\n\n    public static void main(String[] args) throws IOException {\n        // 生成字节码\n        byte[] classFile = ProxyGenerator.generateProxyClass(\"$Proxy11\", UserServiceImpl.class.getInterfaces());\n        // 写入到磁盘\n        IOUtils.write(classFile, new FileOutputStream(\"/Users/yunai/ls/$Proxy11.class\"));\n    }\n\n}\n"
  },
  {
    "path": "lab-69-proxy/lab-69-proxy-jdk/src/main/java/cn/iocoder/springboot/labs/lab69/TestProxyMain.java",
    "content": "package cn.iocoder.springboot.labs.lab69;\n\nimport cn.iocoder.springboot.labs.lab69.handler.UserServiceHandler;\nimport cn.iocoder.springboot.labs.lab69.service.UserService;\nimport cn.iocoder.springboot.labs.lab69.service.UserServiceImpl;\n\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Proxy;\n\n/**\n * 使用 JDK {@link Proxy} 实现一个动态代理的示例\n */\npublic class TestProxyMain {\n\n    public static void main(String[] args) {\n        // 创建 UserService 对象\n        UserService userService = new UserServiceImpl();\n        // JDK 处理器\n        InvocationHandler handler = new UserServiceHandler(userService);\n        // 创建代理\n        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(TestProxyMain.class.getClassLoader(), userService.getClass().getInterfaces(), handler);\n        // 执行\n        userServiceProxy.create(\"yunai\", \"buzhidao\");\n    }\n\n}\n"
  },
  {
    "path": "lab-69-proxy/lab-69-proxy-jdk/src/main/java/cn/iocoder/springboot/labs/lab69/handler/UserServiceHandler.java",
    "content": "package cn.iocoder.springboot.labs.lab69.handler;\n\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\n\npublic class UserServiceHandler implements InvocationHandler {\n\n    /**\n     * 被代理的对象\n     */\n    private final Object object;\n\n    public UserServiceHandler(Object object) {\n        this.object = object;\n    }\n\n    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {\n        System.out.println(\"before invoke\");\n        Object ret = method.invoke(object, args);\n        System.out.println(\"after invoke\");\n        return ret;\n    }\n\n}\n"
  },
  {
    "path": "lab-69-proxy/lab-69-proxy-jdk/src/main/java/cn/iocoder/springboot/labs/lab69/service/UserService.java",
    "content": "package cn.iocoder.springboot.labs.lab69.service;\n\npublic interface UserService {\n\n    void create(String username, String password);\n\n}\n"
  },
  {
    "path": "lab-69-proxy/lab-69-proxy-jdk/src/main/java/cn/iocoder/springboot/labs/lab69/service/UserServiceImpl.java",
    "content": "package cn.iocoder.springboot.labs.lab69.service;\n\npublic class UserServiceImpl implements UserService {\n\n    public void create(String username, String password) {\n        System.out.println(String.format(\"登陆的用户名(%s) 密码(%s)\", username, password));\n    }\n\n}\n"
  },
  {
    "path": "lab-69-proxy/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-69</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-69-proxy-jdk</module>\n        <module>lab-69-proxy-cglib</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "lab-70-db-doc/lab-70-db-doc-screw-01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-70-db-doc</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-70-db-doc-screw-01</artifactId>\n\n    <dependencies>\n        <!-- screw 库，简洁好用的数据库表结构文档生成器 -->\n        <dependency>\n            <groupId>cn.smallbun.screw</groupId>\n            <artifactId>screw-core</artifactId>\n            <version>1.0.5</version>\n        </dependency>\n\n        <!-- 数据库连接 -->\n        <dependency>\n            <groupId>com.zaxxer</groupId>\n            <artifactId>HikariCP</artifactId>\n            <version>3.4.5</version>\n        </dependency>\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>8.0.22</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-70-db-doc/lab-70-db-doc-screw-01/src/main/java/ScrewMain.java",
    "content": "import cn.smallbun.screw.core.Configuration;\nimport cn.smallbun.screw.core.engine.EngineConfig;\nimport cn.smallbun.screw.core.engine.EngineFileType;\nimport cn.smallbun.screw.core.engine.EngineTemplateType;\nimport cn.smallbun.screw.core.execute.DocumentationExecute;\nimport cn.smallbun.screw.core.process.ProcessConfig;\nimport com.zaxxer.hikari.HikariConfig;\nimport com.zaxxer.hikari.HikariDataSource;\n\nimport javax.sql.DataSource;\nimport java.util.Arrays;\nimport java.util.Collections;\n\npublic class ScrewMain {\n\n    private static final String DB_URL = \"jdbc:mysql://400-infra.server.iocoder.cn:3306\";\n    private static final String DB_NAME = \"mall_system\";\n    private static final String DB_USERNAME = \"root\";\n    private static final String DB_PASSWORD = \"3WLiVUBEwTbvAfsh\";\n\n    private static final String FILE_OUTPUT_DIR = \"/Users/yunai/screw_test\";\n    private static final EngineFileType FILE_OUTPUT_TYPE = EngineFileType.HTML; // 可以设置 Word 或者 Markdown 格式\n    private static final String DOC_FILE_NAME = \"数据库文档\";\n    private static final String DOC_VERSION = \"1.0.0\";\n    private static final String DOC_DESCRIPTION = \"文档描述\";\n\n    public static void main(String[] args) {\n        // 创建 screw 的配置\n        Configuration config = Configuration.builder()\n                .version(DOC_VERSION)  // 版本\n                .description(DOC_DESCRIPTION) // 描述\n                .dataSource(buildDataSource()) // 数据源\n                .engineConfig(buildEngineConfig()) // 引擎配置\n                .produceConfig(buildProcessConfig()) // 处理配置\n                .build();\n\n        // 执行 screw，生成数据库文档\n        new DocumentationExecute(config).execute();\n    }\n\n    /**\n     * 创建数据源\n     */\n    private static DataSource buildDataSource() {\n        // 创建 HikariConfig 配置类\n        HikariConfig hikariConfig = new HikariConfig();\n        hikariConfig.setDriverClassName(\"com.mysql.cj.jdbc.Driver\");\n        hikariConfig.setJdbcUrl(DB_URL + \"/\" + DB_NAME);\n        hikariConfig.setUsername(DB_USERNAME);\n        hikariConfig.setPassword(DB_PASSWORD);\n        hikariConfig.addDataSourceProperty(\"useInformationSchema\", \"true\"); // 设置可以获取 tables remarks 信息\n        // 创建数据源\n        return new HikariDataSource(hikariConfig);\n    }\n\n    /**\n     * 创建 screw 的引擎配置\n     */\n    private static EngineConfig buildEngineConfig() {\n        return EngineConfig.builder()\n                .fileOutputDir(FILE_OUTPUT_DIR) // 生成文件路径\n                .openOutputDir(false) // 打开目录\n                .fileType(FILE_OUTPUT_TYPE) // 文件类型\n                .produceType(EngineTemplateType.freemarker) // 文件类型\n                .fileName(DOC_FILE_NAME) // 自定义文件名称\n                .build();\n    }\n\n    /**\n     * 创建 screw 的处理配置，一般可忽略\n     * 指定生成逻辑、当存在指定表、指定表前缀、指定表后缀时，将生成指定表，其余表不生成、并跳过忽略表配置\n     */\n    private static ProcessConfig buildProcessConfig() {\n        return ProcessConfig.builder()\n                .designatedTableName(Collections.<String>emptyList())  // 根据名称指定表生成\n                .designatedTablePrefix(Collections.<String>emptyList()) //根据表前缀生成\n                .designatedTableSuffix(Collections.<String>emptyList()) // 根据表后缀生成\n                .ignoreTableName(Arrays.asList(\"test_user\", \"test_group\")) // 忽略表名\n                .ignoreTablePrefix(Collections.singletonList(\"test_\")) // 忽略表前缀\n                .ignoreTableSuffix(Collections.singletonList(\"_test\")) // 忽略表后缀\n                .build();\n    }\n\n}\n"
  },
  {
    "path": "lab-70-db-doc/lab-70-db-doc-screw-02/doc/测试文档名称.html",
    "content": "﻿<html lang=\"zh\"><head><title>数据库文档</title><style type='text/css'>body {            padding-bottom: 50px        }        body, td {            font-family: verdana, fantasy;            font-size: 12px;            line-height: 150%        }        table {            width: 100%;            background-color: #ccc;            margin: 5px 0        }        td {            background-color: #fff;            padding: 3px 3px 3px 10px        }        thead td {            text-align: center;            font-weight: bold;            background-color: #eee        }        a:link, a:visited, a:active {            color: #015fb6;            text-decoration: none        }        a:hover {            color: #e33e06        }</style></head><body style='text-align:center;'><div style='width:800px; margin:20px auto; text-align:left;'><a name='index'></a><h2 style='text-align:center; line-height:50px;'>数据库文档</h2><div><b>数据库名：mall_system</b><br><b>文档版本：1.0-SNAPSHOT</b><br><b>文档描述：数据库文档生成</b><br></div><table cellspacing='1'><thead><tr><td style='width:40px; '>序号</td><td>表名</td><td>说明</td></tr></thead><tr><td style='text-align:center;'>1</td><td><a href='#admin'>admin</a></td><td>管理员</td></tr><tr><td style='text-align:center;'>2</td><td><a href='#admin_department'>admin_department</a></td><td>部门</td></tr><tr><td style='text-align:center;'>3</td><td><a href='#oauth2_access_token'>oauth2_access_token</a></td><td>访问令牌</td></tr><tr><td style='text-align:center;'>4</td><td><a href='#oauth2_refresh_token'>oauth2_refresh_token</a></td><td>刷新令牌</td></tr><tr><td style='text-align:center;'>5</td><td><a href='#permission_admin_role'>permission_admin_role</a></td><td>管理员角色</td></tr><tr><td style='text-align:center;'>6</td><td><a href='#permission_resource'>permission_resource</a></td><td>资源</td></tr><tr><td style='text-align:center;'>7</td><td><a href='#permission_role'>permission_role</a></td><td>角色</td></tr><tr><td style='text-align:center;'>8</td><td><a href='#permission_role_resource'>permission_role_resource</a></td><td>角色资源</td></tr><tr><td style='text-align:center;'>9</td><td><a href='#system_access_log'>system_access_log</a></td><td>系统访问日志</td></tr><tr><td style='text-align:center;'>10</td><td><a href='#system_data_dict'>system_data_dict</a></td><td>数据字典</td></tr><tr><td style='text-align:center;'>11</td><td><a href='#system_error_code'>system_error_code</a></td><td>错误码</td></tr><tr><td style='text-align:center;'>12</td><td><a href='#system_exception_log'>system_exception_log</a></td><td>系统异常日志</td></tr><tr><td style='text-align:center;'>13</td><td><a href='#_operation_log'>_operation_log</a></td><td>操作日志</td></tr></table><a name='admin'></a><div style='margin-top:30px;'><a href='#index'                                         style='float:right; margin-top:6px;'>返回目录</a><b>表名：admin</b></div><div>说明：管理员</div><div>数据列：</div><table cellspacing='1'><thead><tr><td style='width:40px; '>序号</td><td>名称</td><td>数据类型</td><td>长度</td><td>小数位</td><td>允许空值</td><td>主键</td><td>默认值</td><td>说明</td></tr></thead><tr><td style='text-align:center;'>1</td><td>id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>Y</td><td align='center'></td><td align='center'>管理员编号</td></tr><tr><td style='text-align:center;'>2</td><td>name</td><td align='center'>varchar</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>真实名字</td></tr><tr><td style='text-align:center;'>3</td><td>avatar</td><td align='center'>varchar</td><td align='center'>255</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>头像</td></tr><tr><td style='text-align:center;'>4</td><td>department_id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>部门id</td></tr><tr><td style='text-align:center;'>5</td><td>status</td><td align='center'>tinyint</td><td align='center'>4</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>在职状态</td></tr><tr><td style='text-align:center;'>6</td><td>username</td><td align='center'>varchar</td><td align='center'>16</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>登陆账号</td></tr><tr><td style='text-align:center;'>7</td><td>password</td><td align='center'>varchar</td><td align='center'>255</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>加密后的密码</td></tr><tr><td style='text-align:center;'>8</td><td>password_salt</td><td align='center'>varchar</td><td align='center'>64</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>密码的盐</td></tr><tr><td style='text-align:center;'>9</td><td>create_admin_id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>创建管理员编号</td></tr><tr><td style='text-align:center;'>10</td><td>create_ip</td><td align='center'>varchar</td><td align='center'>32</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>创建 IP</td></tr><tr><td style='text-align:center;'>11</td><td>create_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>创建时间</td></tr><tr><td style='text-align:center;'>12</td><td>update_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>最后更新时间</td></tr></table><a name='admin_department'></a><div style='margin-top:30px;'><a href='#index'                                         style='float:right; margin-top:6px;'>返回目录</a><b>表名：admin_department</b></div><div>说明：部门</div><div>数据列：</div><table cellspacing='1'><thead><tr><td style='width:40px; '>序号</td><td>名称</td><td>数据类型</td><td>长度</td><td>小数位</td><td>允许空值</td><td>主键</td><td>默认值</td><td>说明</td></tr></thead><tr><td style='text-align:center;'>1</td><td>id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>Y</td><td align='center'></td><td align='center'>部门编号</td></tr><tr><td style='text-align:center;'>2</td><td>name</td><td align='center'>varchar</td><td align='center'>100</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>部门名称</td></tr><tr><td style='text-align:center;'>3</td><td>sort</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>0</td><td align='center'>排序字段</td></tr><tr><td style='text-align:center;'>4</td><td>pid</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>0</td><td align='center'>父级部门编号</td></tr><tr><td style='text-align:center;'>5</td><td>create_time</td><td align='center'>timestamp</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>创建时间</td></tr><tr><td style='text-align:center;'>6</td><td>update_time</td><td align='center'>timestamp</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>更新时间</td></tr><tr><td style='text-align:center;'>7</td><td>deleted</td><td align='center'>bit</td><td align='center'>1</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>b'0'</td><td align='center'>删除标记</td></tr></table><a name='oauth2_access_token'></a><div style='margin-top:30px;'><a href='#index'                                         style='float:right; margin-top:6px;'>返回目录</a><b>表名：oauth2_access_token</b></div><div>说明：访问令牌</div><div>数据列：</div><table cellspacing='1'><thead><tr><td style='width:40px; '>序号</td><td>名称</td><td>数据类型</td><td>长度</td><td>小数位</td><td>允许空值</td><td>主键</td><td>默认值</td><td>说明</td></tr></thead><tr><td style='text-align:center;'>1</td><td>id</td><td align='center'>varchar</td><td align='center'>32</td><td align='center'>0</td><td align='center'>N</td><td align='center'>Y</td><td align='center'></td><td align='center'>访问令牌</td></tr><tr><td style='text-align:center;'>2</td><td>user_id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>用户编号</td></tr><tr><td style='text-align:center;'>3</td><td>user_type</td><td align='center'>tinyint</td><td align='center'>4</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>用户类型</td></tr><tr><td style='text-align:center;'>4</td><td>refresh_token</td><td align='center'>varchar</td><td align='center'>32</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>刷新令牌</td></tr><tr><td style='text-align:center;'>5</td><td>expires_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>过期时间</td></tr><tr><td style='text-align:center;'>6</td><td>create_ip</td><td align='center'>varchar</td><td align='center'>32</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>创建 IP</td></tr><tr><td style='text-align:center;'>7</td><td>create_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>创建时间</td></tr><tr><td style='text-align:center;'>8</td><td>update_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>最后更新时间</td></tr><tr><td style='text-align:center;'>9</td><td>deleted</td><td align='center'>bit</td><td align='center'>1</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>b'0'</td><td align='center'>是否删除</td></tr></table><a name='oauth2_refresh_token'></a><div style='margin-top:30px;'><a href='#index'                                         style='float:right; margin-top:6px;'>返回目录</a><b>表名：oauth2_refresh_token</b></div><div>说明：刷新令牌</div><div>数据列：</div><table cellspacing='1'><thead><tr><td style='width:40px; '>序号</td><td>名称</td><td>数据类型</td><td>长度</td><td>小数位</td><td>允许空值</td><td>主键</td><td>默认值</td><td>说明</td></tr></thead><tr><td style='text-align:center;'>1</td><td>id</td><td align='center'>varchar</td><td align='center'>32</td><td align='center'>0</td><td align='center'>N</td><td align='center'>Y</td><td align='center'></td><td align='center'>编号，刷新令牌</td></tr><tr><td style='text-align:center;'>2</td><td>user_id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>用户编号</td></tr><tr><td style='text-align:center;'>3</td><td>user_type</td><td align='center'>tinyint</td><td align='center'>4</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>用户类型</td></tr><tr><td style='text-align:center;'>4</td><td>expires_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>过期时间</td></tr><tr><td style='text-align:center;'>5</td><td>create_ip</td><td align='center'>varchar</td><td align='center'>32</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>创建 IP</td></tr><tr><td style='text-align:center;'>6</td><td>create_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>创建时间</td></tr><tr><td style='text-align:center;'>7</td><td>update_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>最后更新时间</td></tr><tr><td style='text-align:center;'>8</td><td>deleted</td><td align='center'>bit</td><td align='center'>1</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>b'0'</td><td align='center'>是否删除</td></tr></table><a name='permission_admin_role'></a><div style='margin-top:30px;'><a href='#index'                                         style='float:right; margin-top:6px;'>返回目录</a><b>表名：permission_admin_role</b></div><div>说明：管理员角色</div><div>数据列：</div><table cellspacing='1'><thead><tr><td style='width:40px; '>序号</td><td>名称</td><td>数据类型</td><td>长度</td><td>小数位</td><td>允许空值</td><td>主键</td><td>默认值</td><td>说明</td></tr></thead><tr><td style='text-align:center;'>1</td><td>id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>Y</td><td align='center'></td><td align='center'>编号</td></tr><tr><td style='text-align:center;'>2</td><td>admin_id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>管理员编号</td></tr><tr><td style='text-align:center;'>3</td><td>role_id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>角色编号</td></tr><tr><td style='text-align:center;'>4</td><td>create_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>创建时间</td></tr><tr><td style='text-align:center;'>5</td><td>update_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>更新时间</td></tr><tr><td style='text-align:center;'>6</td><td>deleted</td><td align='center'>bit</td><td align='center'>1</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'>b'0'</td><td align='center'>是否删除</td></tr></table><a name='permission_resource'></a><div style='margin-top:30px;'><a href='#index'                                         style='float:right; margin-top:6px;'>返回目录</a><b>表名：permission_resource</b></div><div>说明：资源</div><div>数据列：</div><table cellspacing='1'><thead><tr><td style='width:40px; '>序号</td><td>名称</td><td>数据类型</td><td>长度</td><td>小数位</td><td>允许空值</td><td>主键</td><td>默认值</td><td>说明</td></tr></thead><tr><td style='text-align:center;'>1</td><td>id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>Y</td><td align='center'></td><td align='center'>资源编号</td></tr><tr><td style='text-align:center;'>2</td><td>name</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>菜单名</td></tr><tr><td style='text-align:center;'>3</td><td>permission</td><td align='center'>varchar</td><td align='center'>255</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>权限标识</td></tr><tr><td style='text-align:center;'>4</td><td>type</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>资源类型</td></tr><tr><td style='text-align:center;'>5</td><td>sort</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>排序</td></tr><tr><td style='text-align:center;'>6</td><td>pid</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>0</td><td align='center'>父级资源编号(外键：{@link ResourceDO#id})</td></tr><tr><td style='text-align:center;'>7</td><td>route</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>前端路由</td></tr><tr><td style='text-align:center;'>8</td><td>icon</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>菜单图标</td></tr><tr><td style='text-align:center;'>9</td><td>view</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>前端界面</td></tr><tr><td style='text-align:center;'>10</td><td>create_admin_id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>创建管理员编号</td></tr><tr><td style='text-align:center;'>11</td><td>create_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>添加时间</td></tr><tr><td style='text-align:center;'>12</td><td>update_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>更新时间</td></tr><tr><td style='text-align:center;'>13</td><td>deleted</td><td align='center'>bit</td><td align='center'>1</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>b'0'</td><td align='center'>是否删除</td></tr></table><a name='permission_role'></a><div style='margin-top:30px;'><a href='#index'                                         style='float:right; margin-top:6px;'>返回目录</a><b>表名：permission_role</b></div><div>说明：角色</div><div>数据列：</div><table cellspacing='1'><thead><tr><td style='width:40px; '>序号</td><td>名称</td><td>数据类型</td><td>长度</td><td>小数位</td><td>允许空值</td><td>主键</td><td>默认值</td><td>说明</td></tr></thead><tr><td style='text-align:center;'>1</td><td>id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>Y</td><td align='center'></td><td align='center'>角色编号</td></tr><tr><td style='text-align:center;'>2</td><td>name</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>角色名</td></tr><tr><td style='text-align:center;'>3</td><td>code</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>角色编码</td></tr><tr><td style='text-align:center;'>4</td><td>type</td><td align='center'>tinyint</td><td align='center'>4</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>角色类型</td></tr><tr><td style='text-align:center;'>5</td><td>create_admin_id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>创建管理员编号</td></tr><tr><td style='text-align:center;'>6</td><td>create_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>创建时间</td></tr><tr><td style='text-align:center;'>7</td><td>update_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>最后更新时间</td></tr><tr><td style='text-align:center;'>8</td><td>deleted</td><td align='center'>bit</td><td align='center'>1</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>b'0'</td><td align='center'>是否删除</td></tr></table><a name='permission_role_resource'></a><div style='margin-top:30px;'><a href='#index'                                         style='float:right; margin-top:6px;'>返回目录</a><b>表名：permission_role_resource</b></div><div>说明：角色资源</div><div>数据列：</div><table cellspacing='1'><thead><tr><td style='width:40px; '>序号</td><td>名称</td><td>数据类型</td><td>长度</td><td>小数位</td><td>允许空值</td><td>主键</td><td>默认值</td><td>说明</td></tr></thead><tr><td style='text-align:center;'>1</td><td>id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>Y</td><td align='center'></td><td align='center'>编号</td></tr><tr><td style='text-align:center;'>2</td><td>role_id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>-1</td><td align='center'>角色编号(外键：{@link RoleDO}</td></tr><tr><td style='text-align:center;'>3</td><td>resource_id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>-1</td><td align='center'>资源编号</td></tr><tr><td style='text-align:center;'>4</td><td>create_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>创建时间</td></tr><tr><td style='text-align:center;'>5</td><td>update_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>更新时间</td></tr><tr><td style='text-align:center;'>6</td><td>deleted</td><td align='center'>bit</td><td align='center'>1</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>b'0'</td><td align='center'>是否删除</td></tr></table><a name='system_access_log'></a><div style='margin-top:30px;'><a href='#index'                                         style='float:right; margin-top:6px;'>返回目录</a><b>表名：system_access_log</b></div><div>说明：系统访问日志</div><div>数据列：</div><table cellspacing='1'><thead><tr><td style='width:40px; '>序号</td><td>名称</td><td>数据类型</td><td>长度</td><td>小数位</td><td>允许空值</td><td>主键</td><td>默认值</td><td>说明</td></tr></thead><tr><td style='text-align:center;'>1</td><td>id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>Y</td><td align='center'></td><td align='center'>编号</td></tr><tr><td style='text-align:center;'>2</td><td>user_id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>用户编号</td></tr><tr><td style='text-align:center;'>3</td><td>user_type</td><td align='center'>tinyint</td><td align='center'>4</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>用户类型</td></tr><tr><td style='text-align:center;'>4</td><td>trace_id</td><td align='center'>varchar</td><td align='center'>64</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>链路追踪编号</td></tr><tr><td style='text-align:center;'>5</td><td>application_name</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>应用名</td></tr><tr><td style='text-align:center;'>6</td><td>uri</td><td align='center'>varchar</td><td align='center'>4096</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>访问地址</td></tr><tr><td style='text-align:center;'>7</td><td>query_string</td><td align='center'>varchar</td><td align='center'>4096</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>参数</td></tr><tr><td style='text-align:center;'>8</td><td>method</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>http 方法</td></tr><tr><td style='text-align:center;'>9</td><td>user_agent</td><td align='center'>varchar</td><td align='center'>1024</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>userAgent</td></tr><tr><td style='text-align:center;'>10</td><td>ip</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>ip</td></tr><tr><td style='text-align:center;'>11</td><td>start_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>请求时间</td></tr><tr><td style='text-align:center;'>12</td><td>response_time</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>响应时长 -- 毫秒级</td></tr><tr><td style='text-align:center;'>13</td><td>error_code</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>错误码</td></tr><tr><td style='text-align:center;'>14</td><td>error_message</td><td align='center'>varchar</td><td align='center'>512</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>错误提示</td></tr><tr><td style='text-align:center;'>15</td><td>create_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>创建时间</td></tr><tr><td style='text-align:center;'>16</td><td>update_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>最后更新时间</td></tr></table><a name='system_data_dict'></a><div style='margin-top:30px;'><a href='#index'                                         style='float:right; margin-top:6px;'>返回目录</a><b>表名：system_data_dict</b></div><div>说明：数据字典</div><div>数据列：</div><table cellspacing='1'><thead><tr><td style='width:40px; '>序号</td><td>名称</td><td>数据类型</td><td>长度</td><td>小数位</td><td>允许空值</td><td>主键</td><td>默认值</td><td>说明</td></tr></thead><tr><td style='text-align:center;'>1</td><td>id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>Y</td><td align='center'></td><td align='center'>编号</td></tr><tr><td style='text-align:center;'>2</td><td>enum_value</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>大类枚举值</td></tr><tr><td style='text-align:center;'>3</td><td>value</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>小类数值</td></tr><tr><td style='text-align:center;'>4</td><td>display_name</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>展示名</td></tr><tr><td style='text-align:center;'>5</td><td>sort</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>-1</td><td align='center'>排序值</td></tr><tr><td style='text-align:center;'>6</td><td>memo</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>备注</td></tr><tr><td style='text-align:center;'>7</td><td>create_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>创建时间</td></tr><tr><td style='text-align:center;'>8</td><td>update_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>最后更新时间</td></tr><tr><td style='text-align:center;'>9</td><td>deleted</td><td align='center'>bit</td><td align='center'>1</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>b'0'</td><td align='center'>是否删除</td></tr></table><a name='system_error_code'></a><div style='margin-top:30px;'><a href='#index'                                         style='float:right; margin-top:6px;'>返回目录</a><b>表名：system_error_code</b></div><div>说明：错误码</div><div>数据列：</div><table cellspacing='1'><thead><tr><td style='width:40px; '>序号</td><td>名称</td><td>数据类型</td><td>长度</td><td>小数位</td><td>允许空值</td><td>主键</td><td>默认值</td><td>说明</td></tr></thead><tr><td style='text-align:center;'>1</td><td>id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>Y</td><td align='center'></td><td align='center'>错误码编号</td></tr><tr><td style='text-align:center;'>2</td><td>code</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>0</td><td align='center'>错误码编码</td></tr><tr><td style='text-align:center;'>3</td><td>message</td><td align='center'>varchar</td><td align='center'>255</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>错误码错误提示</td></tr><tr><td style='text-align:center;'>4</td><td>type</td><td align='center'>tinyint</td><td align='center'>4</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>错误码类型</td></tr><tr><td style='text-align:center;'>5</td><td>group</td><td align='center'>varchar</td><td align='center'>64</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>错误码分组</td></tr><tr><td style='text-align:center;'>6</td><td>memo</td><td align='center'>varchar</td><td align='center'>255</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>错误码备注</td></tr><tr><td style='text-align:center;'>7</td><td>create_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>创建时间</td></tr><tr><td style='text-align:center;'>8</td><td>update_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>最后更新时间</td></tr><tr><td style='text-align:center;'>9</td><td>deleted</td><td align='center'>bit</td><td align='center'>1</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>b'0'</td><td align='center'>是否删除</td></tr></table><a name='system_exception_log'></a><div style='margin-top:30px;'><a href='#index'                                         style='float:right; margin-top:6px;'>返回目录</a><b>表名：system_exception_log</b></div><div>说明：系统异常日志</div><div>数据列：</div><table cellspacing='1'><thead><tr><td style='width:40px; '>序号</td><td>名称</td><td>数据类型</td><td>长度</td><td>小数位</td><td>允许空值</td><td>主键</td><td>默认值</td><td>说明</td></tr></thead><tr><td style='text-align:center;'>1</td><td>id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>Y</td><td align='center'></td><td align='center'>编号</td></tr><tr><td style='text-align:center;'>2</td><td>user_id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>用户编号</td></tr><tr><td style='text-align:center;'>3</td><td>user_type</td><td align='center'>tinyint</td><td align='center'>4</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>用户类型</td></tr><tr><td style='text-align:center;'>4</td><td>trace_id</td><td align='center'>varchar</td><td align='center'>64</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>链路追踪编号\n     *\n     * 一般来说，通过链路追踪编号，可以将访问日志，错误日志，链路追踪日志，logger 打印日志等，结合在一起，从而进行排错。</td></tr><tr><td style='text-align:center;'>5</td><td>application_name</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>应用名\n     *\n     * 目前读取 spring.application.name</td></tr><tr><td style='text-align:center;'>6</td><td>uri</td><td align='center'>varchar</td><td align='center'>4096</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>访问地址</td></tr><tr><td style='text-align:center;'>7</td><td>query_string</td><td align='center'>varchar</td><td align='center'>4096</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>参数</td></tr><tr><td style='text-align:center;'>8</td><td>method</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>http 方法</td></tr><tr><td style='text-align:center;'>9</td><td>user_agent</td><td align='center'>varchar</td><td align='center'>1024</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>userAgent</td></tr><tr><td style='text-align:center;'>10</td><td>ip</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>ip</td></tr><tr><td style='text-align:center;'>11</td><td>exception_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>异常发生时间</td></tr><tr><td style='text-align:center;'>12</td><td>exception_name</td><td align='center'>varchar</td><td align='center'>128</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>异常名\n     *\n     * {@link Throwable#getClass()} 的类全名</td></tr><tr><td style='text-align:center;'>13</td><td>exception_message</td><td align='center'>text</td><td align='center'>65535</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>异常导致的消息\n     *\n     * {@link cn.iocoder.common.framework.util.ExceptionUtil#getMessage(Throwable)}</td></tr><tr><td style='text-align:center;'>14</td><td>exception_root_cause_message</td><td align='center'>text</td><td align='center'>65535</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>异常导致的根消息\n     *\n     * {@link cn.iocoder.common.framework.util.ExceptionUtil#getRootCauseMessage(Throwable)}</td></tr><tr><td style='text-align:center;'>15</td><td>exception_stack_trace</td><td align='center'>text</td><td align='center'>65535</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>异常的栈轨迹\n     *\n     * {@link cn.iocoder.common.framework.util.ExceptionUtil#getServiceException(Exception)}</td></tr><tr><td style='text-align:center;'>16</td><td>exception_class_name</td><td align='center'>varchar</td><td align='center'>512</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>异常发生的类全名\n     *\n     * {@link StackTraceElement#getClassName()}</td></tr><tr><td style='text-align:center;'>17</td><td>exception_file_name</td><td align='center'>varchar</td><td align='center'>512</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>异常发生的类文件\n     *\n     * {@link StackTraceElement#getFileName()}</td></tr><tr><td style='text-align:center;'>18</td><td>exception_method_name</td><td align='center'>varchar</td><td align='center'>512</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>异常发生的方法名\n     *\n     * {@link StackTraceElement#getMethodName()}</td></tr><tr><td style='text-align:center;'>19</td><td>exception_line_number</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>异常发生的方法所在行\n     *\n     * {@link StackTraceElement#getLineNumber()}</td></tr><tr><td style='text-align:center;'>20</td><td>process_status</td><td align='center'>tinyint</td><td align='center'>4</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>0</td><td align='center'>处理状态</td></tr><tr><td style='text-align:center;'>21</td><td>process_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>处理时间</td></tr><tr><td style='text-align:center;'>22</td><td>process_admin_id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>处理管理员编号</td></tr><tr><td style='text-align:center;'>23</td><td>create_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>创建时间</td></tr><tr><td style='text-align:center;'>24</td><td>update_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>CURRENT_TIMESTAMP</td><td align='center'>最后更新时间</td></tr></table><a name='_operation_log'></a><div style='margin-top:30px;'><a href='#index'                                         style='float:right; margin-top:6px;'>返回目录</a><b>表名：_operation_log</b></div><div>说明：操作日志</div><div>数据列：</div><table cellspacing='1'><thead><tr><td style='width:40px; '>序号</td><td>名称</td><td>数据类型</td><td>长度</td><td>小数位</td><td>允许空值</td><td>主键</td><td>默认值</td><td>说明</td></tr></thead><tr><td style='text-align:center;'>1</td><td>id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>Y</td><td align='center'></td><td align='center'>编号</td></tr><tr><td style='text-align:center;'>2</td><td>trace_id</td><td align='center'>varchar</td><td align='center'>64</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>链路追踪编号</td></tr><tr><td style='text-align:center;'>3</td><td>account_id</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'>-1</td><td align='center'>账号编号</td></tr><tr><td style='text-align:center;'>4</td><td>application_name</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>应用名</td></tr><tr><td style='text-align:center;'>5</td><td>uri</td><td align='center'>varchar</td><td align='center'>4096</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>访问地址</td></tr><tr><td style='text-align:center;'>6</td><td>params</td><td align='center'>varchar</td><td align='center'>4096</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>参数</td></tr><tr><td style='text-align:center;'>7</td><td>method</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>http 方法</td></tr><tr><td style='text-align:center;'>8</td><td>user_agent</td><td align='center'>varchar</td><td align='center'>1024</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>userAgent</td></tr><tr><td style='text-align:center;'>9</td><td>ip</td><td align='center'>varchar</td><td align='center'>50</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>ip</td></tr><tr><td style='text-align:center;'>10</td><td>start_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>请求时间</td></tr><tr><td style='text-align:center;'>11</td><td>response_time</td><td align='center'>int</td><td align='center'>10</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>响应时长 -- 毫秒级</td></tr><tr><td style='text-align:center;'>12</td><td>msg</td><td align='center'>varchar</td><td align='center'>255</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>日志消息</td></tr><tr><td style='text-align:center;'>13</td><td>status</td><td align='center'>bit</td><td align='center'>1</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>操作状态</td></tr><tr><td style='text-align:center;'>14</td><td>operator</td><td align='center'>varchar</td><td align='center'>64</td><td align='center'>0</td><td align='center'>Y</td><td align='center'>N</td><td align='center'></td><td align='center'>创建者</td></tr><tr><td style='text-align:center;'>15</td><td>create_time</td><td align='center'>datetime</td><td align='center'>19</td><td align='center'>0</td><td align='center'>N</td><td align='center'>N</td><td align='center'></td><td align='center'>创建时间</td></tr></table></div><footer></footer></body></html>"
  },
  {
    "path": "lab-70-db-doc/lab-70-db-doc-screw-02/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-70-db-doc</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-70-db-doc-screw-02</artifactId>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>cn.smallbun.screw</groupId>\n                <artifactId>screw-maven-plugin</artifactId>\n                <version>1.0.5</version>\n                <dependencies>\n                    <!-- 数据库连接 -->\n                    <dependency>\n                        <groupId>com.zaxxer</groupId>\n                        <artifactId>HikariCP</artifactId>\n                        <version>3.4.5</version>\n                    </dependency>\n                    <dependency>\n                        <groupId>mysql</groupId>\n                        <artifactId>mysql-connector-java</artifactId>\n                        <version>8.0.22</version>\n                    </dependency>\n                </dependencies>\n                <configuration>\n                    <!-- 数据库相关配置 -->\n                    <driverClassName>com.mysql.cj.jdbc.Driver</driverClassName>\n                    <jdbcUrl>jdbc:mysql://400-infra.server.iocoder.cn:3306/mall_system</jdbcUrl>\n                    <username>root</username>\n                    <password>3WLiVUBEwTbvAfsh</password>\n                    <!-- screw 配置 -->\n                    <fileType>HTML</fileType>\n                    <title>数据库文档</title> <!--标题-->\n                    <fileName>测试文档名称</fileName> <!--文档名称 为空时:将采用[数据库名称-描述-版本号]作为文档名称-->\n                    <description>数据库文档生成</description> <!--描述-->\n                    <version>${project.version}</version> <!--版本-->\n                    <openOutputDir>false</openOutputDir> <!--打开文件输出目录-->\n                    <produceType>freemarker</produceType> <!--生成模板-->\n                </configuration>\n                <executions>\n                    <execution>\n                        <phase>compile</phase>\n                        <goals>\n                            <goal>run</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "lab-70-db-doc/lab-70-db-doc-screw-03/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-70-db-doc</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-70-db-doc-screw-03</artifactId>\n\n    <dependencies>\n        <!-- screw 库，简洁好用的数据库表结构文档生成器 -->\n        <dependency>\n            <groupId>cn.smallbun.screw</groupId>\n            <artifactId>screw-core</artifactId>\n            <version>1.0.5</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.smallbun.screw</groupId>\n            <artifactId>screw-extension</artifactId>\n            <version>1.0.5</version>\n        </dependency>\n\n        <!-- 数据库连接 -->\n        <dependency>\n            <groupId>com.zaxxer</groupId>\n            <artifactId>HikariCP</artifactId>\n            <version>3.4.5</version>\n        </dependency>\n        <dependency>\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>8.0.22</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-70-db-doc/lab-70-db-doc-screw-03/src/main/java/ScrewMain.java",
    "content": "import cn.smallbun.screw.core.process.ProcessConfig;\nimport cn.smallbun.screw.extension.pojo.PojoConfiguration;\nimport cn.smallbun.screw.extension.pojo.execute.PojoExecute;\nimport cn.smallbun.screw.extension.pojo.strategy.HumpNameStrategy;\nimport com.zaxxer.hikari.HikariConfig;\nimport com.zaxxer.hikari.HikariDataSource;\n\nimport javax.sql.DataSource;\nimport java.util.Arrays;\nimport java.util.Collections;\n\npublic class ScrewMain {\n\n    private static final String DB_URL = \"jdbc:mysql://400-infra.server.iocoder.cn:3306\";\n    private static final String DB_NAME = \"mall_system\";\n    private static final String DB_USERNAME = \"root\";\n    private static final String DB_PASSWORD = \"3WLiVUBEwTbvAfsh\";\n\n    private static final String FILE_OUTPUT_DIR = \"/Users/yunai/screw_test\";\n    private static final String JAVA_CLASS_PACKAGE = \"cn.iocoder.dataobject\";\n\n    public static void main(String[] args) {\n        // 创建 screw 的配置\n        PojoConfiguration config = PojoConfiguration.builder()\n                .path(FILE_OUTPUT_DIR) // 生成 POJO 相关的目录\n                .packageName(JAVA_CLASS_PACKAGE) // 包名\n                .nameStrategy(new HumpNameStrategy()) // 包名策略\n                .useLombok(false) // 是否使用 Lombok\n                .dataSource(buildDataSource()) // 数据源\n                .processConfig(buildProcessConfig()) // 处理配置\n                .build();\n\n        // 执行 screw，生成 POJO 实体类\n        new PojoExecute(config).execute();\n    }\n\n    /**\n     * 创建数据源\n     */\n    private static DataSource buildDataSource() {\n        // 创建 HikariConfig 配置类\n        HikariConfig hikariConfig = new HikariConfig();\n        hikariConfig.setDriverClassName(\"com.mysql.cj.jdbc.Driver\");\n        hikariConfig.setJdbcUrl(DB_URL + \"/\" + DB_NAME);\n        hikariConfig.setUsername(DB_USERNAME);\n        hikariConfig.setPassword(DB_PASSWORD);\n        hikariConfig.addDataSourceProperty(\"useInformationSchema\", \"true\"); // 设置可以获取 tables remarks 信息\n        // 创建数据源\n        return new HikariDataSource(hikariConfig);\n    }\n\n    /**\n     * 创建 screw 的处理配置，一般可忽略\n     * 指定生成逻辑、当存在指定表、指定表前缀、指定表后缀时，将生成指定表，其余表不生成、并跳过忽略表配置\n     */\n    private static ProcessConfig buildProcessConfig() {\n        return ProcessConfig.builder()\n                .designatedTableName(Collections.<String>emptyList())  // 根据名称指定表生成\n                .designatedTablePrefix(Collections.<String>emptyList()) //根据表前缀生成\n                .designatedTableSuffix(Collections.<String>emptyList()) // 根据表后缀生成\n                .ignoreTableName(Arrays.asList(\"test_user\", \"test_group\")) // 忽略表名\n                .ignoreTablePrefix(Collections.singletonList(\"test_\")) // 忽略表前缀\n                .ignoreTableSuffix(Collections.singletonList(\"_test\")) // 忽略表后缀\n                .build();\n    }\n\n}\n"
  },
  {
    "path": "lab-70-db-doc/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-70-db-doc</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>lab-70-db-doc-screw-01</module>\n        <module>lab-70-db-doc-screw-02</module>\n        <module>lab-70-db-doc-screw-03</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-70-db-doc/《芋道 Spring Boot 数据表结构文档》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/DB-Doc-screw/?github>\n"
  },
  {
    "path": "lab-71-http-debug/lab-71-idea-http-client/http-client.env.json",
    "content": "{\n  \"local\": {\n    \"baseUrl\": \"http://127.0.0.1:8080\"\n  },\n  \"dev\": {\n    \"baseUrl\": \"http://192.168.100.200:8080\"\n  }\n}\n"
  },
  {
    "path": "lab-71-http-debug/lab-71-idea-http-client/http-client.private.env.json",
    "content": "{\n  \"local\": {\n    \"username\": \"yudaoyuanma\",\n    \"password\": \"123456\",\n    \"token\": \"token001\"\n  },\n  \"dev\": {\n    \"username\": \"yutou\",\n    \"password\": \"buzhidao\",\n    \"token\": \"aoteman\"\n  }\n}\n"
  },
  {
    "path": "lab-71-http-debug/lab-71-idea-http-client/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.2.1.RELEASE</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-71-idea-http-client</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-71-http-debug/lab-71-idea-http-client/src/main/java/cn/iocoder/springboot/lab71/Application.java",
    "content": "package cn.iocoder.springboot.lab71;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-71-http-debug/lab-71-idea-http-client/src/main/java/cn/iocoder/springboot/lab71/controller/UserController.http",
    "content": "### 测试 /user/login：登陆成功\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n### 测试 /user/get-current：获取成功\nGET http://127.0.0.1:8080/user/get-current?full=true\nAuthorization: token001\n\n### 测试 /user/update：更新成功\nPOST http://127.0.0.1:8080/user/update\nContent-Type: application/json\n\n{\n  \"nickname\": \"我是昵称\",\n  \"gender\": 1\n}\n\n"
  },
  {
    "path": "lab-71-http-debug/lab-71-idea-http-client/src/main/java/cn/iocoder/springboot/lab71/controller/UserController.java",
    "content": "package cn.iocoder.springboot.lab71.controller;\n\nimport cn.iocoder.springboot.lab71.vo.UserUpdateVO;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.*;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 用户 Controller\n *\n * 示例代码，纯粹为了演示。\n */\n@RestController\npublic class UserController {\n\n    private final Logger logger = LoggerFactory.getLogger(getClass());\n\n    @PostMapping(\"/user/login\")\n    public Map<String, Object> login(@RequestParam(\"username\") String username,\n                                     @RequestParam(\"password\") String password) {\n        if (\"yudaoyuanma\".equals(username) && \"123456\".equals(password)) {\n            Map<String, Object> tokenMap = new HashMap<>();\n            tokenMap.put(\"userId\", 1);\n            tokenMap.put(\"token\", \"token001\");\n            return tokenMap;\n        }\n        throw new RuntimeException(\"小朋友，你的账号密码不正确哟！\");\n    }\n\n    @GetMapping(\"/user/get-current\")\n    public Map<String, Object> getCurrentUser(@RequestHeader(\"Authorization\") String authorization,\n                                              @RequestParam(\"full\") boolean full) {\n        if (\"token001\".equals(authorization)) {\n            Map<String, Object> userInfo = new HashMap<>();\n            userInfo.put(\"id\", 1);\n            // full 为 true 时，获得完整信息\n            if (full) {\n                userInfo.put(\"nickname\", \"芋道源码\");\n                userInfo.put(\"gender\", 1);\n            }\n            return userInfo;\n        }\n        throw new RuntimeException(\"小朋友，你没有登录哟！\");\n    }\n\n    @PostMapping(\"/user/update\")\n    public Boolean update(@RequestBody UserUpdateVO updateVO) {\n        logger.info(\"[update][收到更新请求：{}]\", updateVO.toString());\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "lab-71-http-debug/lab-71-idea-http-client/src/main/java/cn/iocoder/springboot/lab71/controller/UserController2.http",
    "content": "### 测试 /user/login：登陆成功\nPOST {{baseUrl}}/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername={{username}}&password={{password}}\n\n### 测试 /user/get-current：获取成功\nGET {{baseUrl}}/user/get-current?full=true\nAuthorization: {{token}}\n\n### 测试 /user/update：更新成功\nPOST {{baseUrl}}/user/update\nContent-Type: application/json\n\n{\n  \"nickname\": \"我是昵称\",\n  \"gender\": 1\n}\n\n"
  },
  {
    "path": "lab-71-http-debug/lab-71-idea-http-client/src/main/java/cn/iocoder/springboot/lab71/controller/UserController3.http",
    "content": "### 001 测试 /user/login：登陆成功\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n> {%\nclient.test(\"验证登陆成功\", function (){\n  client.assert(response.status === 200, \"响应状态应该是 200，结果是 \" + response.status)\n  client.assert(response.body.userId === 1, \"响应的 userId 应该是 1，结果是 \" + response.body.userId)\n  client.assert(response.body.token === \"token001\", \"响应的 token 应该是 token001，记过是 \" + response.body.token)\n});\n%}\n\n### 002 测试 /user/login：登陆失败，密码不正确\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=buzhidao\n\n> {%\nclient.test(\"验证登陆失败\", function (){\n  client.assert(response.status === 200, \"响应状态应该是 200，结果是 \" + response.status)\n});\n%}\n"
  },
  {
    "path": "lab-71-http-debug/lab-71-idea-http-client/src/main/java/cn/iocoder/springboot/lab71/controller/UserController4.http",
    "content": "### 测试 /user/login：登陆成功\nPOST http://127.0.0.1:8080/user/login\nContent-Type: application/x-www-form-urlencoded\n\nusername=yudaoyuanma&password=123456\n\n> {%\nclient.global.set(\"token_from_server\", response.body.token)\n%}\n\n### 测试 /user/get-current：获取成功\nGET http://127.0.0.1:8080/user/get-current?full=true\nAuthorization: {{token_from_server}}\n"
  },
  {
    "path": "lab-71-http-debug/lab-71-idea-http-client/src/main/java/cn/iocoder/springboot/lab71/vo/UserUpdateVO.java",
    "content": "package cn.iocoder.springboot.lab71.vo;\n\n/**\n * 用户更新 VO\n */\npublic class UserUpdateVO {\n\n    /**\n     * 昵称\n     */\n    private String nickname;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public String getNickname() {\n        return nickname;\n    }\n\n    public UserUpdateVO setNickname(String nickname) {\n        this.nickname = nickname;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserUpdateVO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n    @Override\n    public String toString() {\n        return \"UserUpdateVO{\" +\n                \"nickname='\" + nickname + '\\'' +\n                \", gender=\" + gender +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "lab-71-http-debug/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-71-http-debug</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>lab-71-idea-http-client</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "lab-71-http-debug/《芋道 Spring Boot API 接口调试 IDEA HTTP Client》.md",
    "content": "<http://www.iocoder.cn/Spring-Boot/IDEA-HTTP-Client/?github>\n"
  },
  {
    "path": "lab-72-minio/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-parent</artifactId>\n        <version>2.6.4</version>\n        <relativePath/> <!-- lookup parent from repository -->\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>lab-72-minio</artifactId>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- MinIO 客户端 -->\n        <dependency>\n            <groupId>io.minio</groupId>\n            <artifactId>minio</artifactId>\n            <version>8.2.2</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "lab-72-minio/src/main/java/cn/iocoder/springboot/lab72/MinIOApplication.java",
    "content": "package cn.iocoder.springboot.lab72;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class MinIOApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(MinIOApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "lab-72-minio/src/main/java/cn/iocoder/springboot/lab72/config/MinIOConfiguration.java",
    "content": "package cn.iocoder.springboot.lab72.config;\n\nimport io.minio.MinioClient;\nimport org.springframework.context.annotation.*;\n\n@Configuration\npublic class MinIOConfiguration {\n\n    @Bean\n    public MinioClient minioClient() {\n        // Minio 配置。实际项目中，定义到 application.yml 配置文件中\n        String endpoint = \"http://127.0.0.1:9000\";\n        String accessKey = \"admin\";\n        String secretKey = \"password\";\n\n        // 创建 MinioClient 客户端\n        return MinioClient.builder()\n                .endpoint(endpoint)\n                .credentials(accessKey, secretKey)\n                .build();\n    }\n\n}\n"
  },
  {
    "path": "lab-72-minio/src/main/java/cn/iocoder/springboot/lab72/controller/FileController.java",
    "content": "package cn.iocoder.springboot.lab72.controller;\n\nimport io.minio.*;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.multipart.MultipartFile;\n\nimport javax.annotation.Resource;\nimport java.util.UUID;\n\n@RestController\n@RequestMapping(\"/file\")\npublic class FileController {\n\n    @Resource\n    private MinioClient minioClient;\n\n    // Minio 配置。实际项目中，定义到 application.yml 配置文件中\n    private String endpoint = \"http://127.0.0.1:9000\";\n    private String bucket = \"yudaoyuanma\";\n\n    /**\n     * 上传文件\n     */\n    @PostMapping(\"/upload\")\n    public String upload(@RequestParam(\"file\") MultipartFile file) throws Exception {\n        // 上传\n        String path = UUID.randomUUID().toString(); // 文件名，使用 UUID 随机\n        minioClient.putObject(PutObjectArgs.builder()\n                .bucket(bucket) // 存储桶\n                .object(path) // 文件名\n                .stream(file.getInputStream(), file.getSize(), -1) // 文件内容\n                .contentType(file.getContentType()) // 文件类型\n                .build());\n        // 拼接路径\n        return String.format(\"%s/%s/%s\", endpoint, bucket, path);\n    }\n\n    /**\n     * 删除文件\n     */\n    @DeleteMapping(\"/delete\")\n    public void delete(@RequestParam(\"path\") String path) throws Exception {\n        minioClient.removeObject(RemoveObjectArgs.builder()\n                .bucket(bucket) // 存储桶\n                .object(path) // 文件名\n                .build());\n    }\n\n}\n"
  },
  {
    "path": "lab-72-minio/《芋道 Spring Boot 对象存储 MinIO 入门》.md",
    "content": "<https://www.iocoder.cn/Spring-Boot/MinIO/?github>\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo01-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-01-spring-cloud-alibaba-nacos-discovery</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-01-sca-nacos-discovery-demo01-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo01-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx01/nacosdemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx01.nacosdemo.consumer;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.List;\n\n@SpringBootApplication\n// @EnableDiscoveryClient\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n    @Configuration\n    public class RestTemplateConfiguration {\n\n        @Bean\n        public RestTemplate restTemplate() {\n            return new RestTemplate();\n        }\n\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private DiscoveryClient discoveryClient;\n        @Autowired\n        private RestTemplate restTemplate;\n        @Autowired\n        private LoadBalancerClient loadBalancerClient;\n\n        @GetMapping(\"/hello\")\n        public String hello(String name) {\n            // 获得服务 `demo-provider` 的一个实例\n            ServiceInstance instance;\n            if (true) {\n                // 获取服务 `demo-provider` 对应的实例列表\n                List<ServiceInstance> instances = discoveryClient.getInstances(\"demo-provider\");\n                // 选择第一个\n                instance = instances.size() > 0 ? instances.get(0) : null;\n            } else {\n                instance = loadBalancerClient.choose(\"demo-provider\");\n            }\n            // 发起调用\n            if (instance == null) {\n                throw new IllegalStateException(\"获取不到实例\");\n            }\n            String targetUrl = instance.getUri() + \"/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo01-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo01-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-01-spring-cloud-alibaba-nacos-discovery</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-01-sca-nacos-discovery-demo01-provider</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo01-provider/src/main/java/cn/iocoder/springcloudalibaba/labx01/nacosdemo/provider/DemoProviderApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx01.nacosdemo.provider;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class DemoProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoProviderApplication.class, args);\n    }\n\n    @RestController\n    static class TestController {\n\n        @GetMapping(\"/echo\")\n        public String echo(String name) {\n            return \"provider:\" + name;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo01-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。\n\nserver:\n  port: 18080 # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo02-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-01-spring-cloud-alibaba-nacos-discovery</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-01-sca-nacos-discovery-demo02-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo02-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx01/nacosdemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx01.nacosdemo.consumer;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.List;\n\n@SpringBootApplication\n// @EnableDiscoveryClient\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n    @Configuration\n    public class RestTemplateConfiguration {\n\n        @Bean\n        public RestTemplate restTemplate() {\n            return new RestTemplate();\n        }\n\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private DiscoveryClient discoveryClient;\n        @Autowired\n        private RestTemplate restTemplate;\n        @Autowired\n        private LoadBalancerClient loadBalancerClient;\n\n        @GetMapping(\"/hello\")\n        public String hello(String name) {\n            // 获得服务 `demo-provider` 的一个实例\n            ServiceInstance instance;\n            if (true) {\n                // 获取服务 `demo-provider` 对应的实例列表\n                List<ServiceInstance> instances = discoveryClient.getInstances(\"demo-provider\");\n                // 选择第一个\n                instance = instances.size() > 0 ? instances.get(0) : null;\n            } else {\n                instance = loadBalancerClient.choose(\"demo-provider\");\n            }\n            // 发起调用\n            if (instance == null) {\n                throw new IllegalStateException(\"获取不到实例\");\n            }\n            String targetUrl = instance.getUri() + \"/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo02-consumer/src/main/resources/application-dev.yaml",
    "content": "spring:\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        namespace: 14226a0d-799f-424d-8905-162f6a8bf409 # Nacos 命名空间 dev 的编号\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo02-consumer/src/main/resources/application-uat.yaml",
    "content": "spring:\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        namespace: bc8c8c2d-bd85-42bb-ada3-1a8f940ceb20 # Nacos 命名空间 uat 的编号\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo02-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo02-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-01-spring-cloud-alibaba-nacos-discovery</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-01-sca-nacos-discovery-demo02-provider</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo02-provider/src/main/java/cn/iocoder/springcloudalibaba/labx01/nacosdemo/provider/DemoProviderApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx01.nacosdemo.provider;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class DemoProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoProviderApplication.class, args);\n    }\n\n    @RestController\n    static class TestController {\n\n        @GetMapping(\"/echo\")\n        public String echo(String name) {\n            return \"provider:\" + name;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo02-provider/src/main/resources/application-dev.yaml",
    "content": "spring:\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        namespace: 14226a0d-799f-424d-8905-162f6a8bf409 # Nacos 命名空间 dev 的编号\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo02-provider/src/main/resources/application-uat.yaml",
    "content": "spring:\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        namespace: bc8c8c2d-bd85-42bb-ada3-1a8f940ceb20 # Nacos 命名空间 uat 的编号\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo02-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider # Spring 应用名\n\nserver:\n  port: 18080 # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo03-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-01-spring-cloud-alibaba-nacos-discovery</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-01-sca-nacos-discovery-demo03-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo03-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx01/nacosdemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx01.nacosdemo.consumer;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.List;\n\n@SpringBootApplication\n// @EnableDiscoveryClient\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n    @Configuration\n    public class RestTemplateConfiguration {\n\n        @Bean\n        public RestTemplate restTemplate() {\n            return new RestTemplate();\n        }\n\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private DiscoveryClient discoveryClient;\n        @Autowired\n        private RestTemplate restTemplate;\n        @Autowired\n        private LoadBalancerClient loadBalancerClient;\n\n        @GetMapping(\"/hello\")\n        public String hello(String name) {\n            // 获得服务 `demo-provider` 的一个实例\n            ServiceInstance instance;\n            if (true) {\n                // 获取服务 `demo-provider` 对应的实例列表\n                List<ServiceInstance> instances = discoveryClient.getInstances(\"demo-provider\");\n                // 选择第一个\n                instance = instances.size() > 0 ? instances.get(0) : null;\n            } else {\n                instance = loadBalancerClient.choose(\"demo-provider\");\n            }\n            // 发起调用\n            if (instance == null) {\n                throw new IllegalStateException(\"获取不到实例\");\n            }\n            String targetUrl = instance.getUri() + \"/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/labx-01-sca-nacos-discovery-demo03-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n\nmanagement:\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n  endpoint:\n    # Health 端点配置项，对应 HealthProperties 配置类\n    health:\n      enabled: true # 是否开启。默认为 true 开启。\n      show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户；可选 ALWAYS 总是展示。\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-01-spring-cloud-alibaba-nacos-discovery</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-01-sca-nacos-discovery-demo01-provider</module>\n        <module>labx-01-sca-nacos-discovery-demo01-consumer</module>\n\n        <module>labx-01-sca-nacos-discovery-demo02-provider</module>\n        <module>labx-01-sca-nacos-discovery-demo02-consumer</module>\n\n        <module>labx-01-sca-nacos-discovery-demo03-consumer</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-01-spring-cloud-alibaba-nacos-discovery/《芋道 Spring Cloud Alibaba 注册中心 Nacos 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud-Alibaba/Nacos-Discovery/?github>\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo01-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-02-spring-cloud-netflix-ribbon</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-02-scn-ribbon-demo01-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo01-consumer/src/main/java/cn/iocoder/springcloudnetflix/labx02/ribbondemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloudnetflix.labx02.ribbondemo.consumer;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n    @Configuration\n    public class RestTemplateConfiguration {\n\n        @Bean\n        @LoadBalanced\n        public RestTemplate restTemplate() {\n            return new RestTemplate();\n        }\n\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private RestTemplate restTemplate;\n        @Autowired\n        private LoadBalancerClient loadBalancerClient;\n\n        @GetMapping(\"/hello\")\n        public String hello(String name) {\n            // 获得服务 `demo-provider` 的一个实例\n            ServiceInstance instance = loadBalancerClient.choose(\"demo-provider\");\n            // 发起调用\n            String targetUrl = instance.getUri() + \"/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n        @GetMapping(\"/hello02\")\n        public String hello02(String name) {\n            // 直接使用 RestTemplate 调用服务 `demo-provider`\n            String targetUrl = \"http://demo-provider/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo01-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n\n#demo-provider:\n#  ribbon:\n#    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule\n#ribbon:\n#  NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo01-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-02-spring-cloud-netflix-ribbon</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-02-scn-ribbon-demo01-provider</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo01-provider/src/main/java/cn/iocoder/springcloudnetflix/labx02/ribbondemo/provider/DemoProviderApplication.java",
    "content": "package cn.iocoder.springcloudnetflix.labx02.ribbondemo.provider;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@SpringBootApplication\npublic class DemoProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoProviderApplication.class, args);\n    }\n\n    @RestController\n    static class TestController {\n\n        private Logger logger = LoggerFactory.getLogger(TestController.class);\n\n        @Value(\"${server.port}\")\n        private Integer serverPort;\n\n        @GetMapping(\"/echo\")\n        public String echo(String name) throws InterruptedException {\n            // 模拟执行 100ms 时长。方便后续我们测试请求超时\n            Thread.sleep(100L);\n\n            // 记录被调用的日志\n            logger.info(\"[echo][被调用啦 name({})]\", name);\n\n            return serverPort + \"-provider:\" + name;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo01-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: ${random.int[10000,19999]}  # 服务器端口。默认为 8080\n#  port: 18080  # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo02A-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-02-spring-cloud-netflix-ribbon</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-02-scn-ribbon-demo02A-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo02A-consumer/src/main/java/cn/iocoder/springcloudnetflix/labx02/ribbondemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloudnetflix.labx02.ribbondemo.consumer;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n    @Configuration\n    public class RestTemplateConfiguration {\n\n        @Bean\n        @LoadBalanced\n        public RestTemplate restTemplate() {\n            return new RestTemplate();\n        }\n\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private RestTemplate restTemplate;\n        @Autowired\n        private LoadBalancerClient loadBalancerClient;\n\n        @GetMapping(\"/hello\")\n        public String hello(String name) {\n            // 获得服务 `demo-provider` 的一个实例\n            ServiceInstance instance = loadBalancerClient.choose(\"demo-provider\");\n            // 发起调用\n            String targetUrl = instance.getUri() + \"/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n        @GetMapping(\"/hello02\")\n        public String hello02(String name) {\n            // 直接使用 RestTemplate 调用服务 `demo-provider`\n            String targetUrl = \"http://demo-provider/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo02A-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n\n# 设置 Ribbon 客户端的自定义配置\n#ribbon:\n#  NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule\ndemo-provider:\n  ribbon:\n    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则全类名\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo02B-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-02-spring-cloud-netflix-ribbon</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-02-scn-ribbon-demo02B-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo02B-consumer/src/main/java/cn/iocoder/springcloudnetflix/labx02/ribbondemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloudnetflix.labx02.ribbondemo.consumer;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n    @Configuration\n    public class RestTemplateConfiguration {\n\n        @Bean\n        @LoadBalanced\n        public RestTemplate restTemplate() {\n            return new RestTemplate();\n        }\n\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private RestTemplate restTemplate;\n        @Autowired\n        private LoadBalancerClient loadBalancerClient;\n\n        @GetMapping(\"/hello\")\n        public String hello(String name) {\n            // 获得服务 `demo-provider` 的一个实例\n            ServiceInstance instance = loadBalancerClient.choose(\"demo-provider\");\n            // 发起调用\n            String targetUrl = instance.getUri() + \"/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n        @GetMapping(\"/hello02\")\n        public String hello02(String name) {\n            // 直接使用 RestTemplate 调用服务 `demo-provider`\n            String targetUrl = \"http://demo-provider/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n//        @GetMapping(\"/hello03\")\n//        public String hello03(String name) {\n//            // 直接使用 RestTemplate 调用服务 `demo-provider`\n//            String targetUrl = \"http://demo-provider-2/echo?name=\" + name;\n//            String response = restTemplate.getForObject(targetUrl, String.class);\n//            // 返回结果\n//            return \"consumer:\" + response;\n//        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo02B-consumer/src/main/java/cn/iocoder/springcloudnetflix/labx02/ribbondemo/consumer/config/RibbonConfiguration.java",
    "content": "package cn.iocoder.springcloudnetflix.labx02.ribbondemo.consumer.config;\n\nimport org.springframework.cloud.netflix.ribbon.RibbonClient;\nimport org.springframework.cloud.netflix.ribbon.RibbonClients;\nimport org.springframework.context.annotation.Configuration;\nimport ribbon.DefaultRibbonClientConfiguration;\nimport ribbon.UserProviderRibbonClientConfiguration;\n\n@Configuration\n@RibbonClients(\n        value = {\n                @RibbonClient(name = \"demo-provider\", configuration = UserProviderRibbonClientConfiguration.class) // 客户端级别的配置\n        },\n        defaultConfiguration = DefaultRibbonClientConfiguration.class // 全局配置\n)\npublic class RibbonConfiguration {\n}\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo02B-consumer/src/main/java/ribbon/DefaultRibbonClientConfiguration.java",
    "content": "package ribbon;\n\nimport com.netflix.loadbalancer.IRule;\nimport com.netflix.loadbalancer.RoundRobinRule;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class DefaultRibbonClientConfiguration {\n\n    @Bean\n    public IRule ribbonDefaultRule() {\n        return new RoundRobinRule();\n    }\n\n}\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo02B-consumer/src/main/java/ribbon/UserProviderRibbonClientConfiguration.java",
    "content": "package ribbon;\n\nimport com.netflix.loadbalancer.IRule;\nimport com.netflix.loadbalancer.RandomRule;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Primary;\n\n@Configuration\npublic class UserProviderRibbonClientConfiguration {\n\n    @Bean\n    @Primary\n    public IRule ribbonCustomRule() {\n        return new RandomRule();\n    }\n\n}\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo02B-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo03-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-02-spring-cloud-netflix-ribbon</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-02-scn-ribbon-demo03-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo03-consumer/src/main/java/cn/iocoder/springcloudnetflix/labx02/ribbondemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloudnetflix.labx02.ribbondemo.consumer;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n    @Configuration\n    public class RestTemplateConfiguration {\n\n        @Bean\n        @LoadBalanced\n        public RestTemplate restTemplate() {\n            return new RestTemplate();\n        }\n\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private RestTemplate restTemplate;\n        @Autowired\n        private LoadBalancerClient loadBalancerClient;\n\n        @GetMapping(\"/hello\")\n        public String hello(String name) {\n            // 获得服务 `demo-provider` 的一个实例\n            ServiceInstance instance = loadBalancerClient.choose(\"demo-provider\");\n            // 发起调用\n            String targetUrl = instance.getUri() + \"/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n        @GetMapping(\"/hello02\")\n        public String hello02(String name) {\n            // 直接使用 RestTemplate 调用服务 `demo-provider`\n            String targetUrl = \"http://demo-provider/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo03-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n\ndemo-provider:\n  ribbon:\n    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule\n#ribbon:\n#  NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo04-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-02-spring-cloud-netflix-ribbon</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-02-scn-ribbon-demo04-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo04-consumer/src/main/java/cn/iocoder/springcloudnetflix/labx02/ribbondemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloudnetflix.labx02.ribbondemo.consumer;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n    @Configuration\n    public class RestTemplateConfiguration {\n\n        @Bean\n        @LoadBalanced\n        public RestTemplate restTemplate() {\n            return new RestTemplate();\n        }\n\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private RestTemplate restTemplate;\n        @Autowired\n        private LoadBalancerClient loadBalancerClient;\n\n        @GetMapping(\"/hello\")\n        public String hello(String name) {\n            // 获得服务 `demo-provider` 的一个实例\n            ServiceInstance instance = loadBalancerClient.choose(\"demo-provider\");\n            // 发起调用\n            String targetUrl = instance.getUri() + \"/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n        @GetMapping(\"/hello02\")\n        public String hello02(String name) {\n            // 直接使用 RestTemplate 调用服务 `demo-provider`\n            String targetUrl = \"http://demo-provider/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo04-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n\nribbon:\n  # Ribbon 饥饿加载配置项，对应 RibbonEagerLoadProperties 配置类\n  eager-load:\n    enabled: true # 是否开启饥饿加载。默认为 false 不开启\n    clients: user-provider # 开启饥饿加载的 Ribbon 客户端名字。如果有多个，使用 , 逗号分隔。\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo05-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-02-spring-cloud-netflix-ribbon</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-02-scn-ribbon-demo05-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.squareup.okhttp3</groupId>\n            <artifactId>okhttp</artifactId>\n<!--            <version>4.3.1</version>-->\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo05-consumer/src/main/java/cn/iocoder/springcloudnetflix/labx02/ribbondemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloudnetflix.labx02.ribbondemo.consumer;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n    @Configuration\n    public class RestTemplateConfiguration {\n\n        @Bean\n        @LoadBalanced\n        public RestTemplate restTemplate() {\n            return new RestTemplate();\n        }\n\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private RestTemplate restTemplate;\n        @Autowired\n        private LoadBalancerClient loadBalancerClient;\n\n        @GetMapping(\"/hello\")\n        public String hello(String name) {\n            // 获得服务 `demo-provider` 的一个实例\n            ServiceInstance instance = loadBalancerClient.choose(\"demo-provider\");\n            // 发起调用\n            String targetUrl = instance.getUri() + \"/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n        @GetMapping(\"/hello02\")\n        public String hello02(String name) {\n            // 直接使用 RestTemplate 调用服务 `demo-provider`\n            String targetUrl = \"http://demo-provider/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo05-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n\nribbon:\n#  okhttp:\n#    enabled: true # 设置使用 OkHttp，对应 OkHttpRibbonConfiguration 配置类\n  restclient:\n    enabled: true # 设置使用 Jersey Client，对应 RestClientRibbonConfiguration 配置类\n#  httpclient:\n#    enabled: true # 设置使用 Apache HttpClient，对应 HttpClientRibbonConfiguration 配置类\n#  ConnectTimeout: 1 # 请求的连接超时时间，单位：毫秒。默认为 1000\n#  ReadTimeout: 1 # 请求的读取超时时间，单位：毫秒。默认为 1000\n\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo06-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-02-spring-cloud-netflix-ribbon</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-02-scn-ribbon-demo06-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.squareup.okhttp3</groupId>\n            <artifactId>okhttp</artifactId>\n<!--            <version>4.3.1</version>-->\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo06-consumer/src/main/java/cn/iocoder/springcloudnetflix/labx02/ribbondemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloudnetflix.labx02.ribbondemo.consumer;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n    @Configuration\n    public class RestTemplateConfiguration {\n\n        @Bean\n        @LoadBalanced\n        public RestTemplate restTemplate() {\n            return new RestTemplate();\n        }\n\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private RestTemplate restTemplate;\n        @Autowired\n        private LoadBalancerClient loadBalancerClient;\n\n        @GetMapping(\"/hello\")\n        public String hello(String name) {\n            // 获得服务 `demo-provider` 的一个实例\n            ServiceInstance instance = loadBalancerClient.choose(\"demo-provider\");\n            // 发起调用\n            String targetUrl = instance.getUri() + \"/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n        @GetMapping(\"/hello02\")\n        public String hello02(String name) {\n            // 直接使用 RestTemplate 调用服务 `demo-provider`\n            String targetUrl = \"http://demo-provider/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/labx-02-scn-ribbon-demo06-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n    loadbalancer:\n      # Spring Cloud Loadbalancer 重启配置，对应 LoadBalancerRetryProperties 配置类\n      retry:\n        enabled: true # 是否开启重启，默认为 false 关闭重试。\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n\nribbon:\n  restclient:\n    enabled: true # 设置使用 Jersey Client，对应 RestClientRibbonConfiguration 配置类\n\ndemo-provider:\n  ribbon:\n    ConnectTimeout: 1000 # 请求的连接超时时间，单位：毫秒。默认为 1000\n    ReadTimeout: 1 # 请求的读取超时时间，单位：毫秒。默认为 1000\n    OkToRetryOnAllOperations: true # 是否对所有操作都进行重试，默认为 false。\n    MaxAutoRetries: 0 # 对当前服务的重试次数，默认为 0 次。\n    MaxAutoRetriesNextServer: 1 # 重新选择服务实例的次数，默认为 1 次。注意，不包含第 1 次哈。\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-02-spring-cloud-netflix-ribbon</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-02-scn-ribbon-demo01-provider</module>\n        <module>labx-02-scn-ribbon-demo01-consumer</module>\n\n        <module>labx-02-scn-ribbon-demo02A-consumer</module>\n        <module>labx-02-scn-ribbon-demo02B-consumer</module>\n\n        <module>labx-02-scn-ribbon-demo03-consumer</module>\n\n        <module>labx-02-scn-ribbon-demo04-consumer</module>\n\n        <module>labx-02-scn-ribbon-demo05-consumer</module>\n\n        <module>labx-02-scn-ribbon-demo06-consumer</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-02-spring-cloud-netflix-ribbon/《芋道 Spring Cloud Netflix 负载均衡 Ribbon 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud-Netflix/Ribbon/?github>\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-03-spring-cloud-feign</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-03-sc-feign-demo01-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud OpenFeign 相关依赖，使用 OpenFeign 提供声明式调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\n\n@SpringBootApplication\n@EnableFeignClients\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/FeignDemo.java",
    "content": "/**\n * 用于展示纯 Feign 的示例\n */\npackage cn.iocoder.springcloud.labx03.feigndemo.consumer;\n\nimport feign.Feign;\nimport feign.Param;\nimport feign.RequestLine;\n\n// 商品 API\ninterface ProductAPI {\n\n    // 获得商品详情\n    @RequestLine(\"POST /products/{id}\")\n    String get(@Param(\"id\") Integer id);\n\n}\n\npublic class FeignDemo {\n\n    public static void main(String[] args) {\n        // 创建 ProductAPI 对象\n        ProductAPI productAPI = Feign.builder().target(ProductAPI.class,\n                \"http://www.iocoder.cn\"); // 目标地址\n\n        // 调用获得商品\n        String product = productAPI.get(1);\n        System.out.println(product);\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/controller/ConsumerController.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.controller;\n\nimport cn.iocoder.springcloud.labx03.feigndemo.consumer.feign.DemoProviderFeignClient;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ConsumerController {\n\n    @Autowired\n    private DemoProviderFeignClient demoProviderFeignClient;\n\n    @GetMapping(\"/hello02\")\n    public String hello02(String name) {\n        // 使用 Feign 调用接口\n        String response = demoProviderFeignClient.echo(name);\n        // 返回结果\n        return \"consumer:\" + response;\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/feign/DemoProviderFeignClient.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.feign;\n\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(name = \"demo-provider\")\npublic interface DemoProviderFeignClient {\n\n    @GetMapping(\"/echo\")\n    String echo(@RequestParam(\"name\") String name);\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-03-spring-cloud-feign</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-03-sc-feign-demo01-provider</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-provider/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/provider/DemoProviderApplication.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.provider;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoProviderApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-provider/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/provider/controller/ProviderController.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.provider.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ProviderController {\n\n    private Logger logger = LoggerFactory.getLogger(ProviderController.class);\n\n    @Value(\"${server.port}\")\n    private Integer serverPort;\n\n    @GetMapping(\"/echo\")\n    public String echo(String name) throws InterruptedException {\n        // 模拟执行 100ms 时长。方便后续我们测试请求超时\n        Thread.sleep(100L);\n\n        // 记录被调用的日志\n        logger.info(\"[echo][被调用啦 name({})]\", name);\n\n        return serverPort + \"-provider:\" + name;\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo01-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: ${random.int[10000,19999]}  # 服务器端口。默认为 8080\n#  port: 18080  # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo02A-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-03-spring-cloud-feign</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-03-sc-feign-demo02A-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud OpenFeign 相关依赖，使用 OpenFeign 提供声明式调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo02A-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\n\n@SpringBootApplication\n@EnableFeignClients\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo02A-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/controller/ConsumerController.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.controller;\n\nimport cn.iocoder.springcloud.labx03.feigndemo.consumer.feign.DemoProviderFeignClient;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ConsumerController {\n\n    @Autowired\n    private DemoProviderFeignClient demoProviderFeignClient;\n\n    @GetMapping(\"/hello02\")\n    public String hello02(String name) {\n        // 使用 Feign 调用接口\n        String response = demoProviderFeignClient.echo(name);\n        // 返回结果\n        return \"consumer:\" + response;\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo02A-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/feign/DemoProviderFeignClient.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.feign;\n\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(name = \"demo-provider\")\npublic interface DemoProviderFeignClient {\n\n    @GetMapping(\"/echo\")\n    String echo(@RequestParam(\"name\") String name);\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo02A-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n\nlogging:\n  level:\n    cn.iocoder.springcloud.labx03.feigndemo.consumer.feign: DEBUG\n\nfeign:\n  # Feign 客户端配置，对应 FeignClientProperties 配置属性类\n  client:\n    # config 配置项是 Map 类型。key 为 Feign 客户端的名字，value 为 FeignClientConfiguration 对象\n    config:\n      # 全局级别配置\n      default:\n        logger-level: BASIC\n      # 客户端级别配置\n      demo-provider:\n        logger-level: FULL\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo02B-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-03-spring-cloud-feign</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-03-sc-feign-demo02B-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud OpenFeign 相关依赖，使用 OpenFeign 提供声明式调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo02B-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer;\n\nimport cn.iocoder.springcloud.labx03.feigndemo.consumer.config.DefaultFeignClientConfiguration;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\n\n@SpringBootApplication\n@EnableFeignClients(defaultConfiguration = DefaultFeignClientConfiguration.class)\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo02B-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/config/DefaultFeignClientConfiguration.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.config;\n\nimport feign.Logger;\nimport org.springframework.context.annotation.Bean;\n\n/**\n * 全局 FeignClient 配置类\n */\npublic class DefaultFeignClientConfiguration {\n\n    @Bean\n    public Logger.Level defaultFeignClientLoggerLevel() {\n        return Logger.Level.BASIC;\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo02B-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/config/DemoProviderFeignClientConfiguration.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.config;\n\nimport feign.Logger;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Primary;\n\n/**\n * 服务 demo-provider 的 FeignClient 配置类\n */\npublic class DemoProviderFeignClientConfiguration {\n\n    @Bean\n    @Primary // 主 Bean\n    public Logger.Level feignClientLoggerLevel() {\n        return Logger.Level.FULL;\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo02B-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/controller/ConsumerController.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.controller;\n\nimport cn.iocoder.springcloud.labx03.feigndemo.consumer.feign.DemoProviderFeignClient;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ConsumerController {\n\n    @Autowired\n    private DemoProviderFeignClient demoProviderFeignClient;\n\n    @GetMapping(\"/hello02\")\n    public String hello02(String name) {\n        // 使用 Feign 调用接口\n        String response = demoProviderFeignClient.echo(name);\n        // 返回结果\n        return \"consumer:\" + response;\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo02B-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/feign/DemoProviderFeignClient.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.feign;\n\nimport cn.iocoder.springcloud.labx03.feigndemo.consumer.config.DemoProviderFeignClientConfiguration;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(name = \"demo-provider\", configuration = DemoProviderFeignClientConfiguration.class)\npublic interface DemoProviderFeignClient {\n\n    @GetMapping(\"/echo\")\n    String echo(@RequestParam(\"name\") String name);\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo02B-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n\nlogging:\n  level:\n    cn.iocoder.springcloud.labx03.feigndemo.consumer.feign: DEBUG\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-03-spring-cloud-feign</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-03-sc-feign-demo03-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 DemoProvider API 包 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-03-sc-feign-demo03-provider-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud OpenFeign 相关依赖，使用 OpenFeign 提供声明式调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\n\n@SpringBootApplication\n@EnableFeignClients\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/controller/ConsumerController.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.controller;\n\nimport cn.iocoder.springcloud.labx03.feigndemo.consumer.feign.DemoProviderFeignClient;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ConsumerController {\n\n    @Autowired\n    private DemoProviderFeignClient demoProviderFeignClient;\n\n    @GetMapping(\"/hello02\")\n    public String hello02(String name) {\n        // 使用 Feign 调用接口\n        String response = demoProviderFeignClient.echo(name);\n        // 返回结果\n        return \"consumer:\" + response;\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/feign/DemoProviderFeignClient.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.feign;\n\nimport cn.iocoder.springcloud.labx03.feigndemo.provider.api.ProviderService;\nimport org.springframework.cloud.openfeign.FeignClient;\n\n@FeignClient(name = \"demo-provider\")\npublic interface DemoProviderFeignClient extends ProviderService {\n\n//    @GetMapping(\"/echo\")\n//    String echo(@RequestParam(\"name\") String name);\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-03-spring-cloud-feign</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-03-sc-feign-demo03-provider</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 DemoProvider API 包 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-03-sc-feign-demo03-provider-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-provider/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/provider/DemoProviderApplication.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.provider;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoProviderApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-provider/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/provider/controller/ProviderController.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.provider.controller;\n\nimport cn.iocoder.springcloud.labx03.feigndemo.provider.api.ProviderService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ProviderController implements ProviderService {\n\n    private Logger logger = LoggerFactory.getLogger(ProviderController.class);\n\n    @Value(\"${server.port}\")\n    private Integer serverPort;\n\n    @Override\n    public String echo(String name) {\n        // 模拟执行 100ms 时长。方便后续我们测试请求超时\n        try {\n            Thread.sleep(100L);\n        } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n        }\n\n        // 记录被调用的日志\n        logger.info(\"[echo][被调用啦 name({})]\", name);\n\n        return serverPort + \"-provider:\" + name;\n    }\n\n//    @GetMapping(\"/echo\")\n//    public String echo(String name) throws InterruptedException {\n//        // 模拟执行 100ms 时长。方便后续我们测试请求超时\n//        Thread.sleep(100L);\n//\n//        // 记录被调用的日志\n//        logger.info(\"[echo][被调用啦 name({})]\", name);\n//\n//        return serverPort + \"-provider:\" + name;\n//    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: ${random.int[10000,19999]}  # 服务器端口。默认为 8080\n#  port: 18080  # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-provider-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-03-spring-cloud-feign</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-03-sc-feign-demo03-provider-api</artifactId>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 依赖 -->\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-web</artifactId>\n            <version>5.2.3.RELEASE</version>\n            <scope>provided</scope> <!-- scope 为 provided 提供级别即可，真正版本的依赖，交给服务提供者和消费者 -->\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo03-provider-api/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/provider/api/ProviderService.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.provider.api;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\npublic interface ProviderService {\n\n    @GetMapping(\"/echo\")\n    String echo(@RequestParam(\"name\") String name);\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-03-spring-cloud-feign</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-03-sc-feign-demo04-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud OpenFeign 相关依赖，使用 OpenFeign 提供声明式调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\n\n@SpringBootApplication\n@EnableFeignClients\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/controller/ConsumerController.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.controller;\n\nimport cn.iocoder.springcloud.labx03.feigndemo.consumer.dto.DemoDTO;\nimport cn.iocoder.springcloud.labx03.feigndemo.consumer.feign.DemoProviderFeignClient;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@RestController\npublic class ConsumerController {\n\n    @Autowired\n    private DemoProviderFeignClient demoProviderFeignClient;\n\n    @GetMapping(\"/hello02\")\n    public String hello02(String name) {\n        // 使用 Feign 调用接口\n        String response = demoProviderFeignClient.echo(name);\n        // 返回结果\n        return \"consumer:\" + response;\n    }\n\n    @GetMapping(\"/test_get_demo\")\n    public DemoDTO testGetDemo(@RequestParam(\"type\") int type, DemoDTO demoDTO) {\n        // 方式一\n        if (type == 1) {\n            return demoProviderFeignClient.getDemo(demoDTO);\n        } else if (type == 2) {\n            return demoProviderFeignClient.getDemo(demoDTO.getUsername(), demoDTO.getPassword());\n        } else {\n            // 方式三\n            Map<String, Object> params = new HashMap<>();\n            params.put(\"username\", demoDTO.getUsername());\n            params.put(\"password\", demoDTO.getPassword());\n            return demoProviderFeignClient.getDemo(params);\n        }\n    }\n\n    @GetMapping(\"/test_post_demo\")\n    public DemoDTO testPostDemo(DemoDTO demoDTO) {\n       return demoProviderFeignClient.postDemo(demoDTO);\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/dto/DemoDTO.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.dto;\n\npublic class DemoDTO {\n\n    private String username;\n    private String password;\n\n    public String getUsername() {\n        return username;\n    }\n\n    public DemoDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public DemoDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/feign/DemoProviderFeignClient.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.feign;\n\nimport cn.iocoder.springcloud.labx03.feigndemo.consumer.dto.DemoDTO;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.cloud.openfeign.SpringQueryMap;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestParam;\n\nimport java.util.Map;\n\n@FeignClient(name = \"demo-provider\")\npublic interface DemoProviderFeignClient {\n\n    @GetMapping(\"/echo\")\n    String echo(@RequestParam(\"name\") String name);\n\n    @GetMapping(\"/get_demo\") // GET 方式一，最推荐\n    DemoDTO getDemo(@SpringQueryMap DemoDTO demoDTO);\n\n    @GetMapping(\"/get_demo\") // GET 方式二，相对推荐\n    DemoDTO getDemo(@RequestParam(\"username\") String username, @RequestParam(\"password\") String password);\n\n    @GetMapping(\"/get_demo\") // GET 方式三，不推荐\n    DemoDTO getDemo(@RequestParam Map<String, Object> params);\n\n    @PostMapping(\"/post_demo\") // POST 方式\n    DemoDTO postDemo(@RequestBody DemoDTO demoDTO);\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-03-spring-cloud-feign</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-03-sc-feign-demo04-provider</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-provider/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/provider/DemoProviderApplication.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.provider;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoProviderApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-provider/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/provider/controller/ProviderController.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.provider.controller;\n\nimport cn.iocoder.springcloud.labx03.feigndemo.provider.dto.DemoDTO;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ProviderController {\n\n    private Logger logger = LoggerFactory.getLogger(ProviderController.class);\n\n    @Value(\"${server.port}\")\n    private Integer serverPort;\n\n    @GetMapping(\"/echo\")\n    public String echo(String name) throws InterruptedException {\n        // 模拟执行 100ms 时长。方便后续我们测试请求超时\n        Thread.sleep(100L);\n\n        // 记录被调用的日志\n        logger.info(\"[echo][被调用啦 name({})]\", name);\n\n        return serverPort + \"-provider:\" + name;\n    }\n\n    @GetMapping(\"/get_demo\")\n    public DemoDTO getDemo(DemoDTO demoDTO) {\n        return demoDTO;\n    }\n\n    @PostMapping(\"/post_demo\")\n    public DemoDTO postDemo(@RequestBody DemoDTO demoDTO) {\n        return demoDTO;\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-provider/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/provider/dto/DemoDTO.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.provider.dto;\n\npublic class DemoDTO {\n\n    private String username;\n    private String password;\n\n    public String getUsername() {\n        return username;\n    }\n\n    public DemoDTO setUsername(String username) {\n        this.username = username;\n        return this;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public DemoDTO setPassword(String password) {\n        this.password = password;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo04-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: ${random.int[10000,19999]}  # 服务器端口。默认为 8080\n#  port: 18080  # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo05-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-03-spring-cloud-feign</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-03-sc-feign-demo05-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud OpenFeign 相关依赖，使用 OpenFeign 提供声明式调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo05-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\n\n@SpringBootApplication\n@EnableFeignClients\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo05-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/controller/ConsumerController.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.controller;\n\nimport cn.iocoder.springcloud.labx03.feigndemo.consumer.feign.DemoProviderFeignClient;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ConsumerController {\n\n    @Autowired\n    private DemoProviderFeignClient demoProviderFeignClient;\n\n    @GetMapping(\"/hello02\")\n    public String hello02(String name) {\n        // 使用 Feign 调用接口\n        String response = demoProviderFeignClient.echo(name);\n        // 返回结果\n        return \"consumer:\" + response;\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo05-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/feign/DemoProviderFeignClient.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.feign;\n\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n//@FeignClient(name = \"demo-provider\")\n@FeignClient(name = \"iocoder\", url = \"http://www.iocoder.cn\") // 注意，保持 name 属性和 url 属性的 host 是一致的。\npublic interface DemoProviderFeignClient {\n\n//    @GetMapping(\"/echo\")\n//    String echo(@RequestParam(\"name\") String name);\n\n    @GetMapping(\"/\") // 请求首页\n    String echo(@RequestParam(\"name\") String name);\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo05-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo06A-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-03-spring-cloud-feign</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-03-sc-feign-demo06A-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud OpenFeign 相关依赖，使用 OpenFeign 提供声明式调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n\n        <!-- 引入 Feign Apache HttpClient 依赖 -->\n        <dependency>\n            <groupId>io.github.openfeign</groupId>\n            <artifactId>feign-httpclient</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo06A-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\n\n@SpringBootApplication\n@EnableFeignClients\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo06A-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/controller/ConsumerController.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.controller;\n\nimport cn.iocoder.springcloud.labx03.feigndemo.consumer.feign.DemoProviderFeignClient;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ConsumerController {\n\n    @Autowired\n    private DemoProviderFeignClient demoProviderFeignClient;\n\n    @GetMapping(\"/hello02\")\n    public String hello02(String name) {\n        // 使用 Feign 调用接口\n        String response = demoProviderFeignClient.echo(name);\n        // 返回结果\n        return \"consumer:\" + response;\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo06A-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/feign/DemoProviderFeignClient.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.feign;\n\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(name = \"demo-provider\")\npublic interface DemoProviderFeignClient {\n\n    @GetMapping(\"/echo\")\n    String echo(@RequestParam(\"name\") String name);\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo06A-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n\nfeign:\n  # Feign Apache HttpClient 配置项，对应 FeignHttpClientProperties 配置属性类\n  httpclient:\n    enabled: true # 是否开启。默认为 true\n    max-connections: 200 # 最大连接数。默认为 200\n    max-connections-per-route: 50 # 每个路由的最大连接数。默认为 50。router = host + port\n\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo06B-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-03-spring-cloud-feign</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-03-sc-feign-demo06B-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud OpenFeign 相关依赖，使用 OpenFeign 提供声明式调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n\n        <!-- 引入 Feign OkHttp 依赖 -->\n        <dependency>\n            <groupId>io.github.openfeign</groupId>\n            <artifactId>feign-okhttp</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo06B-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\n\n@SpringBootApplication\n@EnableFeignClients\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo06B-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/controller/ConsumerController.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.controller;\n\nimport cn.iocoder.springcloud.labx03.feigndemo.consumer.feign.DemoProviderFeignClient;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ConsumerController {\n\n    @Autowired\n    private DemoProviderFeignClient demoProviderFeignClient;\n\n    @GetMapping(\"/hello02\")\n    public String hello02(String name) {\n        // 使用 Feign 调用接口\n        String response = demoProviderFeignClient.echo(name);\n        // 返回结果\n        return \"consumer:\" + response;\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo06B-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/feign/DemoProviderFeignClient.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.feign;\n\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(name = \"demo-provider\")\npublic interface DemoProviderFeignClient {\n\n    @GetMapping(\"/echo\")\n    String echo(@RequestParam(\"name\") String name);\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo06B-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n\nfeign:\n  httpclient:\n    enabled: false # 是否开启。默认为 true\n  okhttp:\n    enabled: true # 是否开启。默认为 false\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo07-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-03-spring-cloud-feign</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-03-sc-feign-demo07-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud OpenFeign 相关依赖，使用 OpenFeign 提供声明式调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n\n        <!-- 引入 Feign Apache HttpClient 依赖 -->\n        <dependency>\n            <groupId>io.github.openfeign</groupId>\n            <artifactId>feign-httpclient</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo07-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\n\n@SpringBootApplication\n@EnableFeignClients\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo07-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/controller/ConsumerController.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.controller;\n\nimport cn.iocoder.springcloud.labx03.feigndemo.consumer.feign.DemoProviderFeignClient;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class ConsumerController {\n\n    @Autowired\n    private DemoProviderFeignClient demoProviderFeignClient;\n\n    @GetMapping(\"/hello02\")\n    public String hello02(String name) {\n        // 使用 Feign 调用接口\n        String response = demoProviderFeignClient.echo(name);\n        // 返回结果\n        return \"consumer:\" + response;\n    }\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo07-consumer/src/main/java/cn/iocoder/springcloud/labx03/feigndemo/consumer/feign/DemoProviderFeignClient.java",
    "content": "package cn.iocoder.springcloud.labx03.feigndemo.consumer.feign;\n\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(name = \"demo-provider\")\npublic interface DemoProviderFeignClient {\n\n    @GetMapping(\"/echo\")\n    String echo(@RequestParam(\"name\") String name);\n\n}\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/labx-03-sc-feign-demo07-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n#    loadbalancer:\n#      # Spring Cloud Loadbalancer 重启配置，对应 LoadBalancerRetryProperties 配置类\n#      retry:\n#        enabled: true # 是否开启重启，默认为 false 关闭重试。\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n\nribbon:\n  ConnectTimeout: 1000 # 请求的连接超时时间，单位：毫秒。默认为 1000\n  ReadTimeout: 1 # 请求的读取超时时间，单位：毫秒。默认为 1000\n  OkToRetryOnAllOperations: true # 是否对所有操作都进行重试，默认为 false。\n  MaxAutoRetries: 0 # 对当前服务的重试次数，默认为 0 次。\n  MaxAutoRetriesNextServer: 1 # 重新选择服务实例的次数，默认为 1 次。注意，不包含第 1 次哈。\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-03-spring-cloud-feign</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-03-sc-feign-demo01-provider</module>\n        <module>labx-03-sc-feign-demo01-consumer</module>\n\n        <module>labx-03-sc-feign-demo02A-consumer</module>\n        <module>labx-03-sc-feign-demo02B-consumer</module>\n\n        <module>labx-03-sc-feign-demo03-provider-api</module>\n        <module>labx-03-sc-feign-demo03-provider</module>\n        <module>labx-03-sc-feign-demo03-consumer</module>\n\n        <module>labx-03-sc-feign-demo04-provider</module>\n        <module>labx-03-sc-feign-demo04-consumer</module>\n\n        <module>labx-03-sc-feign-demo05-consumer</module>\n\n        <module>labx-03-sc-feign-demo06A-consumer</module>\n        <module>labx-03-sc-feign-demo06B-consumer</module>\n\n        <module>labx-03-sc-feign-demo07-consumer</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-03-spring-cloud-feign/《芋道 Spring Cloud 声明式调用 Feign 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/Feign/?github>\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-actuator-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-04-spring-cloud-alibaba-sentinel</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-04-sca-sentinel-actuator-provider</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Sentinel 相关依赖，使用 Sentinel 提供服务保障，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-actuator-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/DemoProviderApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoProviderApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-actuator-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/controller/DemoController.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.controller;\n\nimport com.alibaba.csp.sentinel.Entry;\nimport com.alibaba.csp.sentinel.SphU;\nimport com.alibaba.csp.sentinel.annotation.SentinelResource;\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    // 测试【流量控制】\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return \"echo\";\n    }\n\n    @GetMapping(\"/test\")\n    public String test() {\n        return \"test\";\n    }\n\n    // 测试【熔断降级】\n    @GetMapping(\"/sleep\")\n    public String sleep() throws InterruptedException {\n        Thread.sleep(100L);\n        return \"sleep\";\n    }\n\n    // 测试【热点参数限流】\n    @GetMapping(\"/product_info\")\n    @SentinelResource(\"demo_product_info_hot\")\n    public String productInfo(Integer id) {\n        return \"商品编号：\" + id;\n    }\n\n    // 测试「Sentinel 客户端 API」\n    @GetMapping(\"/entry_demo\")\n    public String entryDemo() {\n        Entry entry = null;\n        try {\n            // <1> 访问资源\n            entry = SphU.entry(\"entry_demo\");\n\n            // <2> ... 执行业务逻辑\n\n            return \"执行成功\";\n        } catch (BlockException ex) { // <3>\n            return \"被拒绝\";\n        } finally {\n            // <4> 释放资源\n            if (entry != null) {\n                entry.exit();\n            }\n        }\n    }\n\n    // 测试「Sentinel @SentinelResource 注解」\n    @GetMapping(\"/annotations_demo\")\n    @SentinelResource(value = \"annotations_demo_resource\",\n            blockHandler = \"blockHandler\",\n            fallback = \"fallback\")\n    public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException {\n        if (id == null) {\n            throw new IllegalArgumentException(\"id 参数不允许为空\");\n        }\n        return \"success...\";\n    }\n\n    // BlockHandler 处理函数，参数最后多一个 BlockException，其余与原函数一致.\n    public String blockHandler(Integer id, BlockException ex) {\n        return \"block：\" + ex.getClass().getSimpleName();\n    }\n\n    // Fallback 处理函数，函数签名与原函数一致或加一个 Throwable 类型的参数.\n    public String fallback(Integer id, Throwable throwable) {\n        return \"fallback：\" + throwable.getMessage();\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-actuator-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomBlockExceptionHandler.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web;\n\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n@Component\npublic class CustomBlockExceptionHandler implements BlockExceptionHandler {\n\n    @Override\n    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {\n        throw e;\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-actuator-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomRequestOriginParser.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web;\n\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.StringUtils;\n\nimport javax.servlet.http.HttpServletRequest;\n\n@Component\npublic class CustomRequestOriginParser implements RequestOriginParser {\n\n    @Override\n    public String parseOrigin(HttpServletRequest request) {\n        // <X> 从 Header 中，获得请求来源\n        String origin = request.getHeader(\"s-user\");\n        // <Y> 如果为空，给一个默认的\n        if (StringUtils.isEmpty(origin)) {\n            origin = \"default\";\n        }\n        return origin;\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-actuator-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/GlobalExceptionHandler.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web;\n\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport com.alibaba.fastjson.JSONObject;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n@Component\n@ControllerAdvice(basePackages = \"cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider\")\npublic class GlobalExceptionHandler {\n\n    @ResponseBody\n    @ExceptionHandler(value = BlockException.class) // 因为这里是示例，所以暂时使用 JSONObject，实际项目最终定义一个 CommonResult。\n    public JSONObject blockExceptionHandler(BlockException blockException) {\n        return new JSONObject().fluentPut(\"code\", 1024)\n            .fluentPut(\"msg\", \"请求被拦截，拦截类型为 \" + blockException.getClass().getSimpleName());\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-actuator-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider\n\n  cloud:\n    # Sentinel 配置项，对应 SentinelProperties 配置属性类\n    sentinel:\n      enabled: true # 是否开启。默认为 true 开启\n      eager: true # 是否饥饿加载。默认为 false 关闭\n      transport:\n        dashboard: 127.0.0.1:7070 # Sentinel 控制台地址\n      filter:\n        url-patterns: /** # 拦截请求的地址。默认为 /*\n\nmanagement:\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n  endpoint:\n    # Health 端点配置项，对应 HealthProperties 配置类\n    health:\n      enabled: true # 是否开启。默认为 true 开启。\n      show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户；可选 ALWAYS 总是展示。\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-apollo-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-04-spring-cloud-alibaba-sentinel</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-04-sca-sentinel-apollo-provider</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Sentinel 相关依赖，使用 Sentinel 提供服务保障，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>\n        </dependency>\n\n        <!-- Sentinel 对 Apollo 作为数据源的支持 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-datasource-apollo</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-apollo-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/DemoProviderApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoProviderApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-apollo-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/controller/DemoController.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.controller;\n\nimport com.alibaba.csp.sentinel.Entry;\nimport com.alibaba.csp.sentinel.SphU;\nimport com.alibaba.csp.sentinel.annotation.SentinelResource;\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    // 测试【流量控制】\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return \"echo\";\n    }\n\n    @GetMapping(\"/test\")\n    public String test() {\n        return \"test\";\n    }\n\n    // 测试【熔断降级】\n    @GetMapping(\"/sleep\")\n    public String sleep() throws InterruptedException {\n        Thread.sleep(100L);\n        return \"sleep\";\n    }\n\n    // 测试【热点参数限流】\n    @GetMapping(\"/product_info\")\n    @SentinelResource(\"demo_product_info_hot\")\n    public String productInfo(Integer id) {\n        return \"商品编号：\" + id;\n    }\n\n    // 测试「Sentinel 客户端 API」\n    @GetMapping(\"/entry_demo\")\n    public String entryDemo() {\n        Entry entry = null;\n        try {\n            // <1> 访问资源\n            entry = SphU.entry(\"entry_demo\");\n\n            // <2> ... 执行业务逻辑\n\n            return \"执行成功\";\n        } catch (BlockException ex) { // <3>\n            return \"被拒绝\";\n        } finally {\n            // <4> 释放资源\n            if (entry != null) {\n                entry.exit();\n            }\n        }\n    }\n\n    // 测试「Sentinel @SentinelResource 注解」\n    @GetMapping(\"/annotations_demo\")\n    @SentinelResource(value = \"annotations_demo_resource\",\n            blockHandler = \"blockHandler\",\n            fallback = \"fallback\")\n    public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException {\n        if (id == null) {\n            throw new IllegalArgumentException(\"id 参数不允许为空\");\n        }\n        return \"success...\";\n    }\n\n    // BlockHandler 处理函数，参数最后多一个 BlockException，其余与原函数一致.\n    public String blockHandler(Integer id, BlockException ex) {\n        return \"block：\" + ex.getClass().getSimpleName();\n    }\n\n    // Fallback 处理函数，函数签名与原函数一致或加一个 Throwable 类型的参数.\n    public String fallback(Integer id, Throwable throwable) {\n        return \"fallback：\" + throwable.getMessage();\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-apollo-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomBlockExceptionHandler.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web;\n\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n@Component\npublic class CustomBlockExceptionHandler implements BlockExceptionHandler {\n\n    @Override\n    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {\n        throw e;\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-apollo-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomRequestOriginParser.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web;\n\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.StringUtils;\n\nimport javax.servlet.http.HttpServletRequest;\n\n@Component\npublic class CustomRequestOriginParser implements RequestOriginParser {\n\n    @Override\n    public String parseOrigin(HttpServletRequest request) {\n        // <X> 从 Header 中，获得请求来源\n        String origin = request.getHeader(\"s-user\");\n        // <Y> 如果为空，给一个默认的\n        if (StringUtils.isEmpty(origin)) {\n            origin = \"default\";\n        }\n        return origin;\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-apollo-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/GlobalExceptionHandler.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web;\n\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport com.alibaba.fastjson.JSONObject;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n@Component\n@ControllerAdvice(basePackages = \"cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider\")\npublic class GlobalExceptionHandler {\n\n    @ResponseBody\n    @ExceptionHandler(value = BlockException.class) // 因为这里是示例，所以暂时使用 JSONObject，实际项目最终定义一个 CommonResult。\n    public JSONObject blockExceptionHandler(BlockException blockException) {\n        return new JSONObject().fluentPut(\"code\", 1024)\n            .fluentPut(\"msg\", \"请求被拦截，拦截类型为 \" + blockException.getClass().getSimpleName());\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-apollo-provider/src/main/resources/application.yaml",
    "content": "server:\n  port: 18080 # 服务器端口，设置为 18080 避免和本地的 Apollo 端口冲突\n\n# Apollo 相关配置项\napp:\n  id: ${spring.application.name} # 使用的 Apollo 的项目（应用）编号\napollo:\n  meta: http://127.0.0.1:8080 # Apollo Meta Server 地址\n  bootstrap:\n    enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。\n    eagerLoad:\n      enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。\n    namespaces: application # 使用的 Apollo 的命名空间，默认为 application。\n\nspring:\n  application:\n    name: demo-provider\n\n  cloud:\n    # Sentinel 配置项，对应 SentinelProperties 配置属性类\n    sentinel:\n      enabled: true # 是否开启。默认为 true 开启\n      eager: true # 是否饥饿加载。默认为 false 关闭\n      transport:\n        dashboard: 127.0.0.1:7070 # Sentinel 控制台地址\n      filter:\n        url-patterns: /** # 拦截请求的地址。默认为 /*\n      # Sentinel 规则的数据源，是一个 Map 类型。key 为数据源名，可自定义；value 为数据源的具体配置\n      datasource:\n        ds1:\n          # 对应 DataSourcePropertiesConfiguration 类\n          apollo:\n            namespaceName: application # Apollo 命名空间\n            flowRulesKey: sentinel.flow-rule # Apollo 配置 key\n            data-type: json # 数据格式\n            rule-type: FLOW # 规则类型\n\n\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-demo01-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-04-spring-cloud-alibaba-sentinel</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-04-sca-sentinel-demo01-provider</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Sentinel 相关依赖，使用 Sentinel 提供服务保障，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-demo01-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/DemoProviderApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoProviderApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-demo01-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/controller/DemoController.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.controller;\n\nimport com.alibaba.csp.sentinel.Entry;\nimport com.alibaba.csp.sentinel.SphU;\nimport com.alibaba.csp.sentinel.annotation.SentinelResource;\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    // 测试【流量控制】\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return \"echo\";\n    }\n\n    @GetMapping(\"/test\")\n    public String test() {\n        return \"test\";\n    }\n\n    // 测试【熔断降级】\n    @GetMapping(\"/sleep\")\n    public String sleep() throws InterruptedException {\n        Thread.sleep(100L);\n        return \"sleep\";\n    }\n\n    // 测试【热点参数限流】\n    @GetMapping(\"/product_info\")\n    @SentinelResource(\"demo_product_info_hot\")\n    public String productInfo(Integer id) {\n        return \"商品编号：\" + id;\n    }\n\n    // 测试「Sentinel 客户端 API」\n    @GetMapping(\"/entry_demo\")\n    public String entryDemo() {\n        Entry entry = null;\n        try {\n            // <1> 访问资源\n            entry = SphU.entry(\"entry_demo\");\n\n            // <2> ... 执行业务逻辑\n\n            return \"执行成功\";\n        } catch (BlockException ex) { // <3>\n            return \"被拒绝\";\n        } finally {\n            // <4> 释放资源\n            if (entry != null) {\n                entry.exit();\n            }\n        }\n    }\n\n    // 测试「Sentinel @SentinelResource 注解」\n    @GetMapping(\"/annotations_demo\")\n    @SentinelResource(value = \"annotations_demo_resource\",\n            blockHandler = \"blockHandler\",\n            fallback = \"fallback\")\n    public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException {\n        if (id == null) {\n            throw new IllegalArgumentException(\"id 参数不允许为空\");\n        }\n        return \"success...\";\n    }\n\n    // BlockHandler 处理函数，参数最后多一个 BlockException，其余与原函数一致.\n    public String blockHandler(Integer id, BlockException ex) {\n        return \"block：\" + ex.getClass().getSimpleName();\n    }\n\n    // Fallback 处理函数，函数签名与原函数一致或加一个 Throwable 类型的参数.\n    public String fallback(Integer id, Throwable throwable) {\n        return \"fallback：\" + throwable.getMessage();\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-demo01-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomBlockExceptionHandler.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web;\n\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n@Component\npublic class CustomBlockExceptionHandler implements BlockExceptionHandler {\n\n    @Override\n    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {\n        throw e;\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-demo01-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomRequestOriginParser.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web;\n\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.StringUtils;\n\nimport javax.servlet.http.HttpServletRequest;\n\n@Component\npublic class CustomRequestOriginParser implements RequestOriginParser {\n\n    @Override\n    public String parseOrigin(HttpServletRequest request) {\n        // <X> 从 Header 中，获得请求来源\n        String origin = request.getHeader(\"s-user\");\n        // <Y> 如果为空，给一个默认的\n        if (StringUtils.isEmpty(origin)) {\n            origin = \"default\";\n        }\n        return origin;\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-demo01-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/GlobalExceptionHandler.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web;\n\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport com.alibaba.fastjson.JSONObject;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n@Component\n@ControllerAdvice(basePackages = \"cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider\")\npublic class GlobalExceptionHandler {\n\n    @ResponseBody\n    @ExceptionHandler(value = BlockException.class) // 因为这里是示例，所以暂时使用 JSONObject，实际项目最终定义一个 CommonResult。\n    public JSONObject blockExceptionHandler(BlockException blockException) {\n        return new JSONObject().fluentPut(\"code\", 1024)\n            .fluentPut(\"msg\", \"请求被拦截，拦截类型为 \" + blockException.getClass().getSimpleName());\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-demo01-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider\n\n  cloud:\n    # Sentinel 配置项，对应 SentinelProperties 配置属性类\n    sentinel:\n      enabled: true # 是否开启。默认为 true 开启\n      eager: true # 是否饥饿加载。默认为 false 关闭\n      transport:\n        dashboard: 127.0.0.1:7070 # Sentinel 控制台地址\n      filter:\n        url-patterns: /** # 拦截请求的地址。默认为 /*\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-feign-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-04-spring-cloud-alibaba-sentinel</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-04-sca-sentinel-feign-consumer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Sentinel 相关依赖，使用 Sentinel 提供服务保障，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud OpenFeign 相关依赖，使用 OpenFeign 提供声明式调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-feign-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\n\n@SpringBootApplication\n@EnableFeignClients\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-feign-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/controller/ConsumerController.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.controller;\n\nimport cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.feign.DemoProviderFeignClient;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/consumer\")\npublic class ConsumerController {\n\n    @Autowired\n    private DemoProviderFeignClient demoProviderFeignClient;\n\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return demoProviderFeignClient.echo();\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-feign-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/fallback/DemoProviderFeignClientFallback.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.fallback;\n\nimport cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.feign.DemoProviderFeignClient;\n\npublic class DemoProviderFeignClientFallback implements DemoProviderFeignClient {\n\n    private Throwable throwable;\n\n    public DemoProviderFeignClientFallback(Throwable throwable) {\n        this.throwable = throwable;\n    }\n\n    @Override\n    public String echo() {\n            return \"fallback:\" + throwable.getClass().getSimpleName();\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-feign-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/fallback/DemoProviderFeignClientFallbackFactory.java",
    "content": "/*\n * Copyright 2013-2018 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      https://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.fallback;\n\nimport feign.hystrix.FallbackFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class DemoProviderFeignClientFallbackFactory implements FallbackFactory<DemoProviderFeignClientFallback> {\n\n\t@Override\n\tpublic DemoProviderFeignClientFallback create(Throwable throwable) {\n\t    // 可以给 DemoProviderFeignClientFallback 提供具体的 throwable 异常\n\t\treturn new DemoProviderFeignClientFallback(throwable);\n\t}\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-feign-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/feign/DemoProviderFeignClient.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.feign;\n\nimport cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.fallback.DemoProviderFeignClientFallbackFactory;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\n\n@FeignClient(name = \"demo-provider\", url = \"http://127.0.0.1:8080\",\n    fallbackFactory = DemoProviderFeignClientFallbackFactory.class)\npublic interface DemoProviderFeignClient {\n\n    @GetMapping(\"/demo/echo\")\n    String echo();\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-feign-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer\n\n  cloud:\n    # Sentinel 配置项，对应 SentinelProperties 配置属性类\n    sentinel:\n      enabled: true # 是否开启。默认为 true 开启\n      eager: true # 是否饥饿加载。默认为 false 关闭\n      transport:\n        dashboard: 127.0.0.1:7070 # Sentinel 控制台地址\n      filter:\n        url-patterns: /** # 拦截请求的地址。默认为 /*\n\nserver:\n  port: 8081\n\nfeign:\n  sentinel:\n    enabled: true # 开启 Sentinel 对 Feign 的支持，默认为 false 关闭。\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-file-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-04-spring-cloud-alibaba-sentinel</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-04-sca-sentinel-file-provider</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Sentinel 相关依赖，使用 Sentinel 提供服务保障，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-file-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/DemoProviderApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoProviderApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-file-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/controller/DemoController.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.controller;\n\nimport com.alibaba.csp.sentinel.Entry;\nimport com.alibaba.csp.sentinel.SphU;\nimport com.alibaba.csp.sentinel.annotation.SentinelResource;\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    // 测试【流量控制】\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return \"echo\";\n    }\n\n    @GetMapping(\"/test\")\n    public String test() {\n        return \"test\";\n    }\n\n    // 测试【熔断降级】\n    @GetMapping(\"/sleep\")\n    public String sleep() throws InterruptedException {\n        Thread.sleep(100L);\n        return \"sleep\";\n    }\n\n    // 测试【热点参数限流】\n    @GetMapping(\"/product_info\")\n    @SentinelResource(\"demo_product_info_hot\")\n    public String productInfo(Integer id) {\n        return \"商品编号：\" + id;\n    }\n\n    // 测试「Sentinel 客户端 API」\n    @GetMapping(\"/entry_demo\")\n    public String entryDemo() {\n        Entry entry = null;\n        try {\n            // <1> 访问资源\n            entry = SphU.entry(\"entry_demo\");\n\n            // <2> ... 执行业务逻辑\n\n            return \"执行成功\";\n        } catch (BlockException ex) { // <3>\n            return \"被拒绝\";\n        } finally {\n            // <4> 释放资源\n            if (entry != null) {\n                entry.exit();\n            }\n        }\n    }\n\n    // 测试「Sentinel @SentinelResource 注解」\n    @GetMapping(\"/annotations_demo\")\n    @SentinelResource(value = \"annotations_demo_resource\",\n            blockHandler = \"blockHandler\",\n            fallback = \"fallback\")\n    public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException {\n        if (id == null) {\n            throw new IllegalArgumentException(\"id 参数不允许为空\");\n        }\n        return \"success...\";\n    }\n\n    // BlockHandler 处理函数，参数最后多一个 BlockException，其余与原函数一致.\n    public String blockHandler(Integer id, BlockException ex) {\n        return \"block：\" + ex.getClass().getSimpleName();\n    }\n\n    // Fallback 处理函数，函数签名与原函数一致或加一个 Throwable 类型的参数.\n    public String fallback(Integer id, Throwable throwable) {\n        return \"fallback：\" + throwable.getMessage();\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-file-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomBlockExceptionHandler.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web;\n\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n@Component\npublic class CustomBlockExceptionHandler implements BlockExceptionHandler {\n\n    @Override\n    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {\n        throw e;\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-file-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomRequestOriginParser.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web;\n\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.StringUtils;\n\nimport javax.servlet.http.HttpServletRequest;\n\n@Component\npublic class CustomRequestOriginParser implements RequestOriginParser {\n\n    @Override\n    public String parseOrigin(HttpServletRequest request) {\n        // <X> 从 Header 中，获得请求来源\n        String origin = request.getHeader(\"s-user\");\n        // <Y> 如果为空，给一个默认的\n        if (StringUtils.isEmpty(origin)) {\n            origin = \"default\";\n        }\n        return origin;\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-file-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/GlobalExceptionHandler.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web;\n\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport com.alibaba.fastjson.JSONObject;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n@Component\n@ControllerAdvice(basePackages = \"cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider\")\npublic class GlobalExceptionHandler {\n\n    @ResponseBody\n    @ExceptionHandler(value = BlockException.class) // 因为这里是示例，所以暂时使用 JSONObject，实际项目最终定义一个 CommonResult。\n    public JSONObject blockExceptionHandler(BlockException blockException) {\n        return new JSONObject().fluentPut(\"code\", 1024)\n            .fluentPut(\"msg\", \"请求被拦截，拦截类型为 \" + blockException.getClass().getSimpleName());\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-file-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider\n\n  cloud:\n    # Sentinel 配置项，对应 SentinelProperties 配置属性类\n    sentinel:\n      enabled: true # 是否开启。默认为 true 开启\n      eager: true # 是否饥饿加载。默认为 false 关闭\n      transport:\n        dashboard: 127.0.0.1:7070 # Sentinel 控制台地址\n      filter:\n        url-patterns: /** # 拦截请求的地址。默认为 /*\n      # Sentinel 规则的数据源，是一个 Map 类型。key 为数据源名，可自定义；value 为数据源的具体配置\n      datasource:\n        ds1:\n          # 对应 DataSourcePropertiesConfiguration 类\n          file:\n            file: /Users/yunai/Sentinel/demo-provider/flow-rule.json # 配置规则所在文件。\n            recommendRefreshMs: 3000 # 定时读取实现刷新，默认为 3000 毫秒。\n            data-type: json # 数据格式\n            rule-type: FLOW # 规则类型\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-nacos-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-04-spring-cloud-alibaba-sentinel</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-04-sca-sentinel-nacos-provider</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Sentinel 相关依赖，使用 Sentinel 提供服务保障，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>\n        </dependency>\n\n        <!-- Sentinel 对 Nacos 作为数据源的支持 -->\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-datasource-nacos</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-nacos-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/DemoProviderApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoProviderApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-nacos-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/controller/DemoController.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.controller;\n\nimport com.alibaba.csp.sentinel.Entry;\nimport com.alibaba.csp.sentinel.SphU;\nimport com.alibaba.csp.sentinel.annotation.SentinelResource;\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    // 测试【流量控制】\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return \"echo\";\n    }\n\n    @GetMapping(\"/test\")\n    public String test() {\n        return \"test\";\n    }\n\n    // 测试【熔断降级】\n    @GetMapping(\"/sleep\")\n    public String sleep() throws InterruptedException {\n        Thread.sleep(100L);\n        return \"sleep\";\n    }\n\n    // 测试【热点参数限流】\n    @GetMapping(\"/product_info\")\n    @SentinelResource(\"demo_product_info_hot\")\n    public String productInfo(Integer id) {\n        return \"商品编号：\" + id;\n    }\n\n    // 测试「Sentinel 客户端 API」\n    @GetMapping(\"/entry_demo\")\n    public String entryDemo() {\n        Entry entry = null;\n        try {\n            // <1> 访问资源\n            entry = SphU.entry(\"entry_demo\");\n\n            // <2> ... 执行业务逻辑\n\n            return \"执行成功\";\n        } catch (BlockException ex) { // <3>\n            return \"被拒绝\";\n        } finally {\n            // <4> 释放资源\n            if (entry != null) {\n                entry.exit();\n            }\n        }\n    }\n\n    // 测试「Sentinel @SentinelResource 注解」\n    @GetMapping(\"/annotations_demo\")\n    @SentinelResource(value = \"annotations_demo_resource\",\n            blockHandler = \"blockHandler\",\n            fallback = \"fallback\")\n    public String annotationsDemo(@RequestParam(required = false) Integer id) throws InterruptedException {\n        if (id == null) {\n            throw new IllegalArgumentException(\"id 参数不允许为空\");\n        }\n        return \"success...\";\n    }\n\n    // BlockHandler 处理函数，参数最后多一个 BlockException，其余与原函数一致.\n    public String blockHandler(Integer id, BlockException ex) {\n        return \"block：\" + ex.getClass().getSimpleName();\n    }\n\n    // Fallback 处理函数，函数签名与原函数一致或加一个 Throwable 类型的参数.\n    public String fallback(Integer id, Throwable throwable) {\n        return \"fallback：\" + throwable.getMessage();\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-nacos-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomBlockExceptionHandler.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web;\n\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.BlockExceptionHandler;\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\n\n@Component\npublic class CustomBlockExceptionHandler implements BlockExceptionHandler {\n\n    @Override\n    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {\n        throw e;\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-nacos-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/CustomRequestOriginParser.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web;\n\nimport com.alibaba.csp.sentinel.adapter.spring.webmvc.callback.RequestOriginParser;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.StringUtils;\n\nimport javax.servlet.http.HttpServletRequest;\n\n@Component\npublic class CustomRequestOriginParser implements RequestOriginParser {\n\n    @Override\n    public String parseOrigin(HttpServletRequest request) {\n        // <X> 从 Header 中，获得请求来源\n        String origin = request.getHeader(\"s-user\");\n        // <Y> 如果为空，给一个默认的\n        if (StringUtils.isEmpty(origin)) {\n            origin = \"default\";\n        }\n        return origin;\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-nacos-provider/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/web/GlobalExceptionHandler.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.web;\n\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport com.alibaba.fastjson.JSONObject;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.bind.annotation.ControllerAdvice;\nimport org.springframework.web.bind.annotation.ExceptionHandler;\nimport org.springframework.web.bind.annotation.ResponseBody;\n\n@Component\n@ControllerAdvice(basePackages = \"cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider\")\npublic class GlobalExceptionHandler {\n\n    @ResponseBody\n    @ExceptionHandler(value = BlockException.class) // 因为这里是示例，所以暂时使用 JSONObject，实际项目最终定义一个 CommonResult。\n    public JSONObject blockExceptionHandler(BlockException blockException) {\n        return new JSONObject().fluentPut(\"code\", 1024)\n            .fluentPut(\"msg\", \"请求被拦截，拦截类型为 \" + blockException.getClass().getSimpleName());\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-nacos-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider\n\n  cloud:\n    # Sentinel 配置项，对应 SentinelProperties 配置属性类\n    sentinel:\n      enabled: true # 是否开启。默认为 true 开启\n      eager: true # 是否饥饿加载。默认为 false 关闭\n      transport:\n        dashboard: 127.0.0.1:7070 # Sentinel 控制台地址\n      filter:\n        url-patterns: /** # 拦截请求的地址。默认为 /*\n      # Sentinel 规则的数据源，是一个 Map 类型。key 为数据源名，可自定义；value 为数据源的具体配置\n      datasource:\n        ds1:\n          # 对应 DataSourcePropertiesConfiguration 类\n          nacos:\n            server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n#            data-id: demo-application-flow-rule\n#            data-id: demo-provider-flow-rule\n            namespace: # Nacos 命名空间\n            group-id: DEFAULT_GROUP # Nacos 分组\n            data-id: ${spring.application.name}-flow-rule # Nacos 配置集编号\n            data-type: json # 数据格式\n            rule-type: FLOW # 规则类型\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-resttemplate-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-04-spring-cloud-alibaba-sentinel</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-04-sca-sentinel-resttemplate-consumer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Sentinel 相关依赖，使用 Sentinel 提供服务保障，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-resttemplate-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-resttemplate-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/config/RestTemplateConfiguration.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.config;\n\nimport com.alibaba.cloud.sentinel.annotation.SentinelRestTemplate;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.client.RestTemplate;\n\n@Configuration\npublic class RestTemplateConfiguration {\n\n    @Bean\n    @SentinelRestTemplate\n    public RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-resttemplate-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx04/sentineldemo/provider/controller/ConsumerController.java",
    "content": "package cn.iocoder.springcloudalibaba.labx04.sentineldemo.provider.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@RestController\n@RequestMapping(\"/consumer\")\npublic class ConsumerController {\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return restTemplate.getForObject(\"http://127.0.0.1:8080/demo/echo\", String.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/labx-04-sca-sentinel-resttemplate-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer\n\n  cloud:\n    # Sentinel 配置项，对应 SentinelProperties 配置属性类\n    sentinel:\n      enabled: true # 是否开启。默认为 true 开启\n      eager: true # 是否饥饿加载。默认为 false 关闭\n      transport:\n        dashboard: 127.0.0.1:7070 # Sentinel 控制台地址\n      filter:\n        url-patterns: /** # 拦截请求的地址。默认为 /*\n\nserver:\n  port: 8081\n\nresttemplate:\n  sentinel:\n    enabled: true # 开启 Sentinel 对 Feign 的支持，默认为 true 开启。\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-04-spring-cloud-alibaba-sentinel</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-04-sca-sentinel-demo01-provider</module>\n\n        <module>labx-04-sca-sentinel-nacos-provider</module>\n        <module>labx-04-sca-sentinel-apollo-provider</module>\n        <module>labx-04-sca-sentinel-file-provider</module>\n\n        <module>labx-04-sca-sentinel-feign-consumer</module>\n        <module>labx-04-sca-sentinel-resttemplate-consumer</module>\n\n        <module>labx-04-sca-sentinel-actuator-provider</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-04-spring-cloud-alibaba-sentinel/《芋道 Spring Cloud Alibaba 服务容错 Sentinel 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud-Alibaba/Sentinel/?github>\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-auto-refresh/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-05-spring-cloud-alibaba-nacos-config</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-05-sca-nacos-config-auto-refresh</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Config 相关依赖，将 Nacos 作为配置中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-auto-refresh/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx5.nacosdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-auto-refresh/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/config/OrderProperties.java",
    "content": "package cn.iocoder.springcloudalibaba.labx5.nacosdemo.config;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n//    public String getDesc() {\n//        return desc;\n//    }\n//\n//    public OrderProperties setDesc(String desc) {\n//        this.desc = desc;\n//        return this;\n//    }\n\n}\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-auto-refresh/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloudalibaba.labx5.nacosdemo.controller;\n\nimport cn.iocoder.springcloudalibaba.labx5.nacosdemo.config.OrderProperties;\nimport com.alibaba.fastjson.JSONObject;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.cloud.context.config.annotation.RefreshScope;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/demo\")\n@RefreshScope\npublic class DemoController {\n\n    @Autowired\n    private OrderProperties orderProperties;\n\n    /**\n     * 测试 @ConfigurationProperties 注解的配置属性类\n     */\n    @GetMapping(\"/test01\")\n    public OrderProperties test01() {\n        return orderProperties;\n    }\n\n    @Value(value = \"${order.pay-timeout-seconds}\")\n    private Integer payTimeoutSeconds;\n    @Value(value = \"${order.create-frequency-seconds}\")\n    private Integer createFrequencySeconds;\n\n    /**\n     * 测试 @Value 注解的属性\n     */\n    @GetMapping(\"/test02\")\n    public Map<String, Object> test02() {\n        return new JSONObject().fluentPut(\"payTimeoutSeconds\", payTimeoutSeconds)\n                .fluentPut(\"createFrequencySeconds\", createFrequencySeconds);\n    }\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/logger\")\n    public void logger() {\n        logger.debug(\"[logger][测试一下]\");\n    }\n\n}\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-auto-refresh/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/listener/DemoEnvironmentChangeListener.java",
    "content": "package cn.iocoder.springcloudalibaba.labx5.nacosdemo.listener;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.context.environment.EnvironmentChangeEvent;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.core.env.ConfigurableEnvironment;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class DemoEnvironmentChangeListener implements ApplicationListener<EnvironmentChangeEvent> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private ConfigurableEnvironment environment;\n\n    @Override\n    public void onApplicationEvent(EnvironmentChangeEvent event) {\n        for (String key : event.getKeys()) {\n            logger.info(\"[onApplicationEvent][key({}) 最新 value 为 {}]\", key, environment.getProperty(key));\n        }\n    }\n\n}\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-auto-refresh/src/main/resources/bootstrap.yaml",
    "content": "spring:\n  application:\n    name: demo-application\n\n  cloud:\n    nacos:\n      # Nacos Config 配置项，对应 NacosConfigProperties 配置属性类\n      config:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        namespace: # 使用的 Nacos 的命名空间，默认为 null\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        name: # 使用的 Nacos 配置集的 dataId，默认为 spring.application.name\n        file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名，同时也是 Nacos 配置集的配置格式，默认为 properties\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-05-spring-cloud-alibaba-nacos-config</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-05-sca-nacos-config-demo</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Config 相关依赖，将 Nacos 作为配置中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx5.nacosdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/config/OrderProperties.java",
    "content": "package cn.iocoder.springcloudalibaba.labx5.nacosdemo.config;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n//@NacosConfigurationProperties(prefix = \"order\", dataId = \"${nacos.config.data-id}\", type = ConfigType.YAML)\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n//    /**\n//     * 配置描述\n//     */\n//    private String desc;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n//    public String getDesc() {\n//        return desc;\n//    }\n//\n//    public OrderProperties setDesc(String desc) {\n//        this.desc = desc;\n//        return this;\n//    }\n\n}\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloudalibaba.labx5.nacosdemo.controller;\n\nimport cn.iocoder.springcloudalibaba.labx5.nacosdemo.config.OrderProperties;\nimport com.alibaba.fastjson.JSONObject;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private OrderProperties orderProperties;\n\n    /**\n     * 测试 @ConfigurationProperties 注解的配置属性类\n     */\n    @GetMapping(\"/test01\")\n    public OrderProperties test01() {\n        return orderProperties;\n    }\n\n    @Value(value = \"${order.pay-timeout-seconds}\") // @NacosValue(value = \"${order.pay-timeout-seconds}\")\n    private Integer payTimeoutSeconds;\n    @Value(value = \"${order.create-frequency-seconds}\") // @NacosValue(value = \"${order.create-frequency-seconds}\")\n    private Integer createFrequencySeconds;\n\n    /**\n     * 测试 @Value 注解的属性\n     */\n    @GetMapping(\"/test02\")\n    public Map<String, Object> test02() {\n        return new JSONObject().fluentPut(\"payTimeoutSeconds\", payTimeoutSeconds)\n                .fluentPut(\"createFrequencySeconds\", createFrequencySeconds);\n    }\n\n}\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo/src/main/resources/bootstrap.yaml",
    "content": "spring:\n  application:\n    name: demo-application\n\n  cloud:\n    nacos:\n      # Nacos Config 配置项，对应 NacosConfigProperties 配置属性类\n      config:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        namespace: # 使用的 Nacos 的命名空间，默认为 null\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        name: # 使用的 Nacos 配置集的 dataId，默认为 spring.application.name\n        file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名，同时也是 Nacos 配置集的配置格式，默认为 properties\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-05-spring-cloud-alibaba-nacos-config</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-05-sca-nacos-config-demo-actuator</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Config 相关依赖，将 Nacos 作为配置中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx5.nacosdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/config/OrderProperties.java",
    "content": "package cn.iocoder.springcloudalibaba.labx5.nacosdemo.config;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n//@NacosConfigurationProperties(prefix = \"order\", dataId = \"${nacos.config.data-id}\", type = ConfigType.YAML)\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n//    /**\n//     * 配置描述\n//     */\n//    private String desc;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n//    public String getDesc() {\n//        return desc;\n//    }\n//\n//    public OrderProperties setDesc(String desc) {\n//        this.desc = desc;\n//        return this;\n//    }\n\n}\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloudalibaba.labx5.nacosdemo.controller;\n\nimport cn.iocoder.springcloudalibaba.labx5.nacosdemo.config.OrderProperties;\nimport com.alibaba.fastjson.JSONObject;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private OrderProperties orderProperties;\n\n    /**\n     * 测试 @ConfigurationProperties 注解的配置属性类\n     */\n    @GetMapping(\"/test01\")\n    public OrderProperties test01() {\n        return orderProperties;\n    }\n\n    @Value(value = \"${order.pay-timeout-seconds}\") // @NacosValue(value = \"${order.pay-timeout-seconds}\")\n    private Integer payTimeoutSeconds;\n    @Value(value = \"${order.create-frequency-seconds}\") // @NacosValue(value = \"${order.create-frequency-seconds}\")\n    private Integer createFrequencySeconds;\n\n    /**\n     * 测试 @Value 注解的属性\n     */\n    @GetMapping(\"/test02\")\n    public Map<String, Object> test02() {\n        return new JSONObject().fluentPut(\"payTimeoutSeconds\", payTimeoutSeconds)\n                .fluentPut(\"createFrequencySeconds\", createFrequencySeconds);\n    }\n\n}\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-actuator/src/main/resources/application.yaml",
    "content": "management:\n  endpoint:\n    # Health 端点配置项，对应 HealthProperties 配置类\n    health:\n      show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户；可选 ALWAYS 总是展示。\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-actuator/src/main/resources/bootstrap.yaml",
    "content": "spring:\n  application:\n    name: demo-application\n\n  cloud:\n    nacos:\n      # Nacos Config 配置项，对应 NacosConfigProperties 配置属性类\n      config:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        namespace: # 使用的 Nacos 的命名空间，默认为 null\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        name: # 使用的 Nacos 配置集的 dataId，默认为 spring.application.name\n        file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名，同时也是 Nacos 配置集的配置格式，默认为 properties\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-jasypt/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-05-spring-cloud-alibaba-nacos-config</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-05-sca-nacos-config-demo-jasypt</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Config 相关依赖，将 Nacos 作为配置中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n\n        <!-- 实现对 Jasypt 实现自动化配置 -->\n        <dependency>\n            <groupId>com.github.ulisesbocchio</groupId>\n            <artifactId>jasypt-spring-boot-starter</artifactId>\n            <version>3.0.2</version>\n            <!--            <scope>test</scope>-->\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-jasypt/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx5.nacosdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-jasypt/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloudalibaba.labx5.nacosdemo.controller;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.cloud.context.config.annotation.RefreshScope;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\n@RefreshScope\npublic class DemoController {\n\n    @Value(\"${xxx-password:}\")\n    private String xxxPassword;\n\n    @GetMapping(\"/test\")\n    public String test() {\n        return xxxPassword;\n    }\n\n}\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-jasypt/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/listener/JasyptEnvironmentChangeListener.java",
    "content": "package cn.iocoder.springcloudalibaba.labx5.nacosdemo.listener;\n\nimport org.apache.commons.lang3.StringUtils;\nimport org.jasypt.encryption.StringEncryptor;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.context.environment.EnvironmentChangeEvent;\nimport org.springframework.cloud.context.environment.EnvironmentManager;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class JasyptEnvironmentChangeListener implements ApplicationListener<EnvironmentChangeEvent> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    // Environment 管理器，可以实现配置项的获取和修改\n    @Autowired\n    private EnvironmentManager environmentManager;\n\n    // Jasypt 加密器，可以对配置项进行加密和加密\n    @Autowired\n    private StringEncryptor encryptor;\n\n    @Override\n    public void onApplicationEvent(EnvironmentChangeEvent event) {\n        for (String key : event.getKeys()) {\n            // 获得 value\n            Object valueObj = environmentManager.getProperty(key);\n            if (!(valueObj instanceof String)) {\n                continue;\n            }\n            String value = (String) valueObj;\n            // 判断 value 是否为加密。如果是，则进行解密\n            if (value.startsWith(\"ENC(\") && value.endsWith(\")\")) {\n                value = encryptor.decrypt(StringUtils.substringBetween(value, \"ENC(\", \")\"));\n                logger.info(\"[onApplicationEvent][key({}) 解密后为 {}]\", key, value);\n                // 设置到 Environment 中\n                environmentManager.setProperty(key, value);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-jasypt/src/main/resources/bootstrap.yaml",
    "content": "spring:\n  application:\n    name: demo-application-jasypt\n\n  cloud:\n    nacos:\n      # Nacos Config 配置项，对应 NacosConfigProperties 配置属性类\n      config:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        namespace: # 使用的 Nacos 的命名空间，默认为 null\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        name: # 使用的 Nacos 配置集的 dataId，默认为 spring.application.name\n        file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名，同时也是 Nacos 配置集的配置格式，默认为 properties\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-jasypt/src/test/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/JasyptTest.java",
    "content": "package cn.iocoder.springcloudalibaba.labx5.nacosdemo;\n\nimport org.jasypt.encryption.StringEncryptor;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class JasyptTest {\n\n    @Autowired\n    private StringEncryptor encryptor;\n\n    @Test\n    public void encode() {\n        // 第一个加密\n        String password = \"woshimima\";\n        System.out.println(encryptor.encrypt(password));\n\n        // 第二个加密\n        password = \"bushimima\";\n        System.out.println(encryptor.encrypt(password));\n    }\n\n    @Value(\"${xxx-password:}\")\n    private String xxxPassword;\n\n    @Test\n    public void print() {\n        System.out.println(xxxPassword);\n    }\n\n//    @Value(\"${jasypt.encryptor.password}\")\n//    private String password;\n\n}\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-multi/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-05-spring-cloud-alibaba-nacos-config</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-05-sca-nacos-config-demo-multi</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Config 相关依赖，将 Nacos 作为配置中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-multi/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx5.nacosdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.core.env.Environment;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);\n\n        // 查看 Environment\n        Environment environment = context.getEnvironment();\n        System.out.println(environment);\n    }\n\n}\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-multi/src/main/resources/bootstrap.yaml",
    "content": "spring:\n  application:\n    name: demo-application\n\n  cloud:\n    nacos:\n      # Nacos Config 配置项，对应 NacosConfigProperties 配置属性类\n      config:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        namespace: # 使用的 Nacos 的命名空间，默认为 null\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        name: ${spring.application.name} # 使用的 Nacos 配置集的 dataId，默认为 spring.application.name\n        file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名，同时也是 Nacos 配置集的配置格式，默认为 properties\n        # 拓展配置集数组，对应 Config 数组\n        extension-configs:\n          - data-id: extension-dataId-01.yaml\n            group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n            refresh: true # 是否自动刷新配置，默认为 false\n          - data-id: extension-dataId-02.yaml\n            group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n            refresh: true # 是否自动刷新配置，默认为 false\n        # 共享配置集数组，对应 Config 数组\n        shared-configs:\n          - data-id: shared-dataId-01.yaml\n            group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n            refresh: true # 是否自动刷新配置，默认为 false\n          - data-id: shared-dataId-02.yaml\n            group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n            refresh: true # 是否自动刷新配置，默认为 false\n\n  profiles:\n    active: dev # 设置开启的 Profiles\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-profiles/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-05-spring-cloud-alibaba-nacos-config</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-05-sca-nacos-config-demo-profiles</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Config 相关依赖，将 Nacos 作为配置中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-profiles/src/main/java/cn/iocoder/springcloudalibaba/labx5/nacosdemo/ProfilesApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx5.nacosdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProfilesApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProfilesApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-profiles/src/main/resources/bootstrap-dev.yaml",
    "content": "spring:\n  cloud:\n    nacos:\n      # Nacos Config 配置项，对应 NacosConfigProperties 配置属性类\n      config:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        namespace: 14226a0d-799f-424d-8905-162f6a8bf409 # 使用的 Nacos 的命名空间，默认为 null\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        name: # 使用的 Nacos 配置集的 dataId，默认为 spring.application.name\n        file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名，同时也是 Nacos 配置集的配置格式，默认为 properties\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-profiles/src/main/resources/bootstrap-prod.yaml",
    "content": "spring:\n  cloud:\n    nacos:\n      # Nacos Config 配置项，对应 NacosConfigProperties 配置属性类\n      config:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        namespace: f1686f3b-a984-4cdf-8298-7caee3455d14 # 使用的 Nacos 的命名空间，默认为 null\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        name: # 使用的 Nacos 配置集的 dataId，默认为 spring.application.name\n        file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名，同时也是 Nacos 配置集的配置格式，默认为 properties\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/labx-05-sca-nacos-config-demo-profiles/src/main/resources/bootstrap.yaml",
    "content": "spring:\n  application:\n    name: demo-application\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-05-spring-cloud-alibaba-nacos-config</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-05-sca-nacos-config-demo</module>\n        <module>labx-05-sca-nacos-config-demo-profiles</module>\n        <module>labx-05-sca-nacos-config-auto-refresh</module>\n        <module>labx-05-sca-nacos-config-demo-jasypt</module>\n        <module>labx-05-sca-nacos-config-demo-actuator</module>\n        <module>labx-05-sca-nacos-config-demo-multi</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-05-spring-cloud-alibaba-nacos-config/《芋道 Spring Cloud Alibaba 配置中心 Nacos 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud-Alibaba/Nacos-Config/?github>\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-06-spring-cloud-stream-rocketmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-06-sca-stream-rocketmq-consumer-actuator</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Stream RocketMQ 相关依赖，将 RocketMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-actuator/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n      # Spring Cloud Stream RocketMQ 配置项\n      rocketmq:\n        # RocketMQ Binder 配置项，对应 RocketMQBinderConfigurationProperties 类\n        binder:\n          name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址\n        # RocketMQ 自定义 Binding 配置项，对应 RocketMQBindingProperties Map\n        bindings:\n          demo01-input:\n            # RocketMQ Consumer 配置项，对应 RocketMQConsumerProperties 类\n            consumer:\n              enabled: true # 是否开启消费，默认为 true\n              broadcasting: false # 是否使用广播消费，默认为 false 使用集群消费\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n\nmanagement:\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n  endpoint:\n    # Health 端点配置项，对应 HealthProperties 配置类\n    health:\n      enabled: true # 是否开启。默认为 true 开启。\n      show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户；可选 ALWAYS 总是展示。\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-aliyun/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-06-spring-cloud-stream-rocketmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-06-sca-stream-rocketmq-consumer-aliyun</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Stream RocketMQ 相关依赖，将 RocketMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-aliyun/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-aliyun/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-aliyun/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-aliyun/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-aliyun/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: TOPIC_YUNAI_TEST # 目的地。这里使用 RocketMQ Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: GID_PRODUCER_GROUP_YUNAI_TEST # 消费者分组\n      # Spring Cloud Stream RocketMQ 配置项\n      rocketmq:\n        # RocketMQ Binder 配置项，对应 RocketMQBinderConfigurationProperties 类\n        binder:\n          name-server: onsaddr.mq-internet-access.mq-internet.aliyuncs.com:80 # RocketMQ Namesrv 地址\n          access-key: ${ALIYUN_ACCESS_KEY} # 阿里云账号 AccessKey\n          secret-key: ${ALIYUN_SECRET_KEY} # 阿里云账号 SecretKey\n        # RocketMQ 自定义 Binding 配置项，对应 RocketMQBindingProperties Map\n        bindings:\n          demo01-input:\n            # RocketMQ Consumer 配置项，对应 RocketMQConsumerProperties 类\n            consumer:\n              enabled: true # 是否开启消费，默认为 true\n              broadcasting: false # 是否使用广播消费，默认为 false 使用集群消费\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-broadcasting/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-06-spring-cloud-stream-rocketmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-06-sca-stream-rocketmq-consumer-broadcasting</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Stream RocketMQ 相关依赖，将 RocketMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-broadcasting/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-broadcasting/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-broadcasting/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-broadcasting/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-broadcasting/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01-X # 消费者分组\n      # Spring Cloud Stream RocketMQ 配置项\n      rocketmq:\n        # RocketMQ Binder 配置项，对应 RocketMQBinderConfigurationProperties 类\n        binder:\n          name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址\n        # RocketMQ 自定义 Binding 配置项，对应 RocketMQBindingProperties Map\n        bindings:\n          demo01-input:\n            # RocketMQ Consumer 配置项，对应 RocketMQConsumerProperties 类\n            consumer:\n              enabled: true # 是否开启消费，默认为 true\n              broadcasting: true # 是否使用广播消费，默认为 false 使用集群消费\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-06-spring-cloud-stream-rocketmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-06-sca-stream-rocketmq-consumer-demo</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Stream RocketMQ 相关依赖，将 RocketMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-demo/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-demo/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-demo/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-demo/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-demo/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n      # Spring Cloud Stream RocketMQ 配置项\n      rocketmq:\n        # RocketMQ Binder 配置项，对应 RocketMQBinderConfigurationProperties 类\n        binder:\n          name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址\n        # RocketMQ 自定义 Binding 配置项，对应 RocketMQBindingProperties Map\n        bindings:\n          demo01-input:\n            # RocketMQ Consumer 配置项，对应 RocketMQConsumerProperties 类\n            consumer:\n              enabled: true # 是否开启消费，默认为 true\n              broadcasting: false # 是否使用广播消费，默认为 false 使用集群消费\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-error-handler/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-06-spring-cloud-stream-rocketmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-06-sca-stream-rocketmq-consumer-error-handler</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Stream RocketMQ 相关依赖，将 RocketMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-error-handler/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-error-handler/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message.Demo01Message;\nimport org.apache.commons.lang3.exception.ExceptionUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.integration.annotation.ServiceActivator;\nimport org.springframework.integration.context.IntegrationContextUtils;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.messaging.support.ErrorMessage;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT) // 对应 DEMO-TOPIC-01.demo01-consumer-group-DEMO-TOPIC-01\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n        // <X> 注意，此处抛出一个 RuntimeException 异常，模拟消费失败\n        throw new RuntimeException(\"我就是故意抛出一个异常\");\n    }\n\n    @ServiceActivator(inputChannel = \"DEMO-TOPIC-01.demo01-consumer-group-DEMO-TOPIC-01.errors\")\n    public void handleError(ErrorMessage errorMessage) {\n        logger.error(\"[handleError][payload：{}]\", ExceptionUtils.getRootCauseMessage(errorMessage.getPayload()));\n        logger.error(\"[handleError][originalMessage：{}]\", errorMessage.getOriginalMessage());\n        logger.error(\"[handleError][headers：{}]\", errorMessage.getHeaders());\n    }\n\n    @StreamListener(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME) // errorChannel\n    public void globalHandleError(ErrorMessage errorMessage) {\n        logger.error(\"[globalHandleError][payload：{}]\", ExceptionUtils.getRootCauseMessage(errorMessage.getPayload()));\n        logger.error(\"[globalHandleError][originalMessage：{}]\", errorMessage.getOriginalMessage());\n        logger.error(\"[globalHandleError][headers：{}]\", errorMessage.getHeaders());\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-error-handler/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-error-handler/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-error-handler/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n          # Consumer 配置项，对应 ConsumerProperties 类\n          consumer:\n            max-attempts: 1\n      # Spring Cloud Stream RocketMQ 配置项\n      rocketmq:\n        # RocketMQ Binder 配置项，对应 RocketMQBinderConfigurationProperties 类\n        binder:\n          name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址\n        # RocketMQ 自定义 Binding 配置项，对应 RocketMQBindingProperties Map\n        bindings:\n          demo01-input:\n            # RocketMQ Consumer 配置项，对应 RocketMQConsumerProperties 类\n            consumer:\n              enabled: true # 是否开启消费，默认为 true\n              broadcasting: false # 是否使用广播消费，默认为 false 使用集群消费\n              delay-level-when-next-consume: 0 # 异步消费消息模式下消费失败重试策略，默认为 0\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-filter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-06-spring-cloud-stream-rocketmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-06-sca-stream-rocketmq-consumer-filter</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Stream RocketMQ 相关依赖，将 RocketMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-filter/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-filter/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n//    @StreamListener(value = MySink.DEMO01_INPUT, condition = \"headers['rocketmq_TAGS'] == 'yunai'\")\n//    public void onMessage(@Payload Demo01Message message) {\n//        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n//    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-filter/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-filter/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-filter/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n      # Spring Cloud Stream RocketMQ 配置项\n      rocketmq:\n        # RocketMQ Binder 配置项，对应 RocketMQBinderConfigurationProperties 类\n        binder:\n          name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址\n        # RocketMQ 自定义 Binding 配置项，对应 RocketMQBindingProperties Map\n        bindings:\n          demo01-input:\n            # RocketMQ Consumer 配置项，对应 RocketMQConsumerProperties 类\n            consumer:\n              enabled: true # 是否开启消费，默认为 true\n              broadcasting: false # 是否使用广播消费，默认为 false 使用集群消费\n              tags: yunai || yutou # 基于 Tag 订阅，多个 Tag 使用 || 分隔，默认为空\n              sql: # 基于 SQL 订阅，默认为空\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-orderly/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-06-spring-cloud-stream-rocketmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-06-sca-stream-rocketmq-consumer-orderly</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Stream RocketMQ 相关依赖，将 RocketMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-orderly/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-orderly/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.Message;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(Message<?> message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-orderly/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-orderly/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-orderly/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n      # Spring Cloud Stream RocketMQ 配置项\n      rocketmq:\n        # RocketMQ Binder 配置项，对应 RocketMQBinderConfigurationProperties 类\n        binder:\n          name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址\n        # RocketMQ 自定义 Binding 配置项，对应 RocketMQBindingProperties Map\n        bindings:\n          demo01-input:\n            # RocketMQ Consumer 配置项，对应 RocketMQConsumerProperties 类\n            consumer:\n              enabled: true # 是否开启消费，默认为 true\n              broadcasting: false # 是否使用广播消费，默认为 false 使用集群消费\n              orderly: true # 是否顺序消费，默认为 false 并发消费。\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-retry/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-06-spring-cloud-stream-rocketmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-06-sca-stream-rocketmq-consumer-retry</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Stream RocketMQ 相关依赖，将 RocketMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-retry/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-retry/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n        // <X> 注意，此处抛出一个 RuntimeException 异常，模拟消费失败\n        throw new RuntimeException(\"我就是故意抛出一个异常\");\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-retry/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-retry/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-consumer-retry/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n          # Consumer 配置项，对应 ConsumerProperties 类\n          consumer:\n            max-attempts: 1\n      # Spring Cloud Stream RocketMQ 配置项\n      rocketmq:\n        # RocketMQ Binder 配置项，对应 RocketMQBinderConfigurationProperties 类\n        binder:\n          name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址\n        # RocketMQ 自定义 Binding 配置项，对应 RocketMQBindingProperties Map\n        bindings:\n          demo01-input:\n            # RocketMQ Consumer 配置项，对应 RocketMQConsumerProperties 类\n            consumer:\n              enabled: true # 是否开启消费，默认为 true\n              broadcasting: false # 是否使用广播消费，默认为 false 使用集群消费\n              delay-level-when-next-consume: 0 # 异步消费消息模式下消费失败重试策略，默认为 0\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-06-spring-cloud-stream-rocketmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-06-sca-stream-rocketmq-producer-actuator</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Stream RocketMQ 相关依赖，将 RocketMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.controller;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.Demo01Message;\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource;\nimport org.apache.rocketmq.common.message.MessageConst;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send\")\n    public boolean send() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .build();\n        // 发送消息\n        return mySource.demo01Output().send(springMessage);\n    }\n\n    @GetMapping(\"/send_delay\")\n    public boolean sendDelay() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .setHeader(MessageConst.PROPERTY_DELAY_TIME_LEVEL, \"3\") // 设置延迟级别为 3，10 秒后消费。\n                .build();\n        // 发送消息\n        boolean sendResult = mySource.demo01Output().send(springMessage);\n        logger.info(\"[sendDelay][发送消息完成, 结果 = {}]\", sendResult);\n        return sendResult;\n    }\n\n    @GetMapping(\"/send_tag\")\n    public boolean sendTag() {\n        for (String tag : new String[]{\"yunai\", \"yutou\", \"tudou\"}) {\n            // 创建 Message\n            Demo01Message message = new Demo01Message()\n                    .setId(new Random().nextInt());\n            // 创建 Spring Message 对象\n            Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                    .setHeader(MessageConst.PROPERTY_TAGS, tag) // 设置 Tag\n                    .build();\n            // 发送消息\n            mySource.demo01Output().send(springMessage);\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/message/MySource.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-actuator/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n      # Spring Cloud Stream RocketMQ 配置项\n      rocketmq:\n        # RocketMQ Binder 配置项，对应 RocketMQBinderConfigurationProperties 类\n        binder:\n          name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址\n        # RocketMQ 自定义 Binding 配置项，对应 RocketMQBindingProperties Map\n        bindings:\n          demo01-output:\n            # RocketMQ Producer 配置项，对应 RocketMQProducerProperties 类\n            producer:\n              group: test # 生产者分组\n              sync: true # 是否同步发送消息，默认为 false 异步。\n\nserver:\n  port: 18080\n\nmanagement:\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n  endpoint:\n    # Health 端点配置项，对应 HealthProperties 配置类\n    health:\n      enabled: true # 是否开启。默认为 true 开启。\n      show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户；可选 ALWAYS 总是展示。\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-aliyun/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-06-spring-cloud-stream-rocketmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-06-sca-stream-rocketmq-producer-aliyun</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Stream RocketMQ 相关依赖，将 RocketMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-aliyun/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-aliyun/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.controller;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.Demo01Message;\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource;\nimport org.apache.rocketmq.common.message.MessageConst;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send\")\n    public boolean send() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .build();\n        // 发送消息\n        return mySource.demo01Output().send(springMessage);\n    }\n\n    @GetMapping(\"/send_delay\")\n    public boolean sendDelay() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .setHeader(MessageConst.PROPERTY_DELAY_TIME_LEVEL, \"3\") // 设置延迟级别为 3，10 秒后消费。\n                .build();\n        // 发送消息\n        boolean sendResult = mySource.demo01Output().send(springMessage);\n        logger.info(\"[sendDelay][发送消息完成, 结果 = {}]\", sendResult);\n        return sendResult;\n    }\n\n    @GetMapping(\"/send_tag\")\n    public boolean sendTag() {\n        for (String tag : new String[]{\"yunai\", \"yutou\", \"tudou\"}) {\n            // 创建 Message\n            Demo01Message message = new Demo01Message()\n                    .setId(new Random().nextInt());\n            // 创建 Spring Message 对象\n            Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                    .setHeader(MessageConst.PROPERTY_TAGS, tag) // 设置 Tag\n                    .build();\n            // 发送消息\n            mySource.demo01Output().send(springMessage);\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-aliyun/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-aliyun/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/message/MySource.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-aliyun/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: TOPIC_YUNAI_TEST # 目的地。这里使用 RocketMQ Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n      # Spring Cloud Stream RocketMQ 配置项\n      rocketmq:\n        # RocketMQ Binder 配置项，对应 RocketMQBinderConfigurationProperties 类\n        binder:\n          name-server: onsaddr.mq-internet-access.mq-internet.aliyuncs.com:80 # RocketMQ Namesrv 地址\n          access-key: ${ALIYUN_ACCESS_KEY} # 阿里云账号 AccessKey\n          secret-key: ${ALIYUN_SECRET_KEY} # 阿里云账号 SecretKey\n        # RocketMQ 自定义 Binding 配置项，对应 RocketMQBindingProperties Map\n        bindings:\n          demo01-output:\n            # RocketMQ Producer 配置项，对应 RocketMQProducerProperties 类\n            producer:\n              group: GID_PRODUCER_GROUP_YUNAI_TEST # 生产者分组\n              sync: true # 是否同步发送消息，默认为 false 异步。\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-06-spring-cloud-stream-rocketmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-06-sca-stream-rocketmq-producer-demo</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Stream RocketMQ 相关依赖，将 RocketMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-demo/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-demo/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.controller;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.Demo01Message;\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource;\nimport org.apache.rocketmq.common.message.MessageConst;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send\")\n    public boolean send() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .build();\n        // 发送消息\n        return mySource.demo01Output().send(springMessage);\n    }\n\n    @GetMapping(\"/send_delay\")\n    public boolean sendDelay() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .setHeader(MessageConst.PROPERTY_DELAY_TIME_LEVEL, \"3\") // 设置延迟级别为 3，10 秒后消费。\n                .build();\n        // 发送消息\n        boolean sendResult = mySource.demo01Output().send(springMessage);\n        logger.info(\"[sendDelay][发送消息完成, 结果 = {}]\", sendResult);\n        return sendResult;\n    }\n\n    @GetMapping(\"/send_tag\")\n    public boolean sendTag() {\n        for (String tag : new String[]{\"yunai\", \"yutou\", \"tudou\"}) {\n            // 创建 Message\n            Demo01Message message = new Demo01Message()\n                    .setId(new Random().nextInt());\n            // 创建 Spring Message 对象\n            Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                    .setHeader(MessageConst.PROPERTY_TAGS, tag) // 设置 Tag\n                    .build();\n            // 发送消息\n            mySource.demo01Output().send(springMessage);\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-demo/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-demo/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/message/MySource.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-demo/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n      # Spring Cloud Stream RocketMQ 配置项\n      rocketmq:\n        # RocketMQ Binder 配置项，对应 RocketMQBinderConfigurationProperties 类\n        binder:\n          name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址\n        # RocketMQ 自定义 Binding 配置项，对应 RocketMQBindingProperties Map\n        bindings:\n          demo01-output:\n            # RocketMQ Producer 配置项，对应 RocketMQProducerProperties 类\n            producer:\n              group: test # 生产者分组\n              sync: true # 是否同步发送消息，默认为 false 异步。\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-orderly/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-06-spring-cloud-stream-rocketmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-06-sca-stream-rocketmq-producer-orderly</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Stream RocketMQ 相关依赖，将 RocketMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-orderly/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-orderly/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.controller;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.Demo01Message;\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource;\nimport org.apache.rocketmq.common.message.MessageConst;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send\")\n    public boolean send() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .build();\n        // 发送消息\n        return mySource.demo01Output().send(springMessage);\n    }\n\n    @GetMapping(\"/send_delay\")\n    public boolean sendDelay() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .setHeader(MessageConst.PROPERTY_DELAY_TIME_LEVEL, \"3\") // 设置延迟级别为 3，10 秒后消费。\n                .build();\n        // 发送消息\n        boolean sendResult = mySource.demo01Output().send(springMessage);\n        logger.info(\"[sendDelay][发送消息完成, 结果 = {}]\", sendResult);\n        return sendResult;\n    }\n\n    @GetMapping(\"/send_orderly\")\n    public boolean sendOrderly() {\n        // 发送 3 条相同 id 的消息\n        int id = new Random().nextInt();\n        for (int i = 0; i < 3; i++) {\n            // 创建 Message\n            Demo01Message message = new Demo01Message().setId(id);\n            // 创建 Spring Message 对象\n            Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                    .build();\n            // 发送消息\n            mySource.demo01Output().send(springMessage);\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-orderly/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-orderly/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/message/MySource.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-orderly/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          # Producer 配置项，对应 ProducerProperties 类\n          producer:\n            partition-key-expression: payload['id'] # 分区 key 表达式。该表达式基于 Spring EL，从消息中获得分区 key。\n      # Spring Cloud Stream RocketMQ 配置项\n      rocketmq:\n        # RocketMQ Binder 配置项，对应 RocketMQBinderConfigurationProperties 类\n        binder:\n          name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址\n        # RocketMQ 自定义 Binding 配置项，对应 RocketMQBindingProperties Map\n        bindings:\n          demo01-output:\n            # RocketMQ Producer 配置项，对应 RocketMQProducerProperties 类\n            producer:\n              group: test # 生产者分组\n              sync: true # 是否同步发送消息，默认为 false 异步。\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-transaction/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-06-spring-cloud-stream-rocketmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-06-sca-stream-rocketmq-producer-transaction</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Stream RocketMQ 相关依赖，将 RocketMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-transaction/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-transaction/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.controller;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.Demo01Message;\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message.MySource;\nimport com.alibaba.fastjson.JSON;\nimport org.apache.rocketmq.common.message.MessageConst;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send\")\n    public boolean send() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .build();\n        // 发送消息\n        return mySource.demo01Output().send(springMessage);\n    }\n\n    @GetMapping(\"/send_delay\")\n    public boolean sendDelay() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .setHeader(MessageConst.PROPERTY_DELAY_TIME_LEVEL, \"3\") // 设置延迟级别为 3，10 秒后消费。\n                .build();\n        // 发送消息\n        boolean sendResult = mySource.demo01Output().send(springMessage);\n        logger.info(\"[sendDelay][发送消息完成, 结果 = {}]\", sendResult);\n        return sendResult;\n    }\n\n    @GetMapping(\"/send_tag\")\n    public boolean sendTag() {\n        for (String tag : new String[]{\"yunai\", \"yutou\", \"tudou\"}) {\n            // 创建 Message\n            Demo01Message message = new Demo01Message()\n                    .setId(new Random().nextInt());\n            // 创建 Spring Message 对象\n            Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                    .setHeader(MessageConst.PROPERTY_TAGS, tag) // 设置 Tag\n                    .build();\n            // 发送消息\n            mySource.demo01Output().send(springMessage);\n        }\n        return true;\n    }\n\n    @GetMapping(\"/send_transaction\")\n    public boolean sendTransaction() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Args args = new Args().setArgs1(1).setArgs2(\"2\");\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .setHeader(\"args\", JSON.toJSONString(args))\n                .build();\n        // 发送消息\n        return mySource.demo01Output().send(springMessage);\n    }\n\n    public static class Args {\n\n        private Integer args1;\n\n        private String args2;\n\n        public Integer getArgs1() {\n            return args1;\n        }\n\n        public Args setArgs1(Integer args1) {\n            this.args1 = args1;\n            return this;\n        }\n\n        public String getArgs2() {\n            return args2;\n        }\n\n        public Args setArgs2(String args2) {\n            this.args2 = args2;\n            return this;\n        }\n\n        @Override\n        public String toString() {\n            return \"Args{\" +\n                    \"args1=\" + args1 +\n                    \", args2='\" + args2 + '\\'' +\n                    '}';\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-transaction/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/listener/TransactionListenerImpl.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.listener;\n\nimport cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.controller.Demo01Controller;\nimport com.alibaba.fastjson.JSON;\nimport org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;\nimport org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;\nimport org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.messaging.Message;\n\n@RocketMQTransactionListener(txProducerGroup = \"test\")\npublic class TransactionListenerImpl implements RocketMQLocalTransactionListener {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {\n        // 从消息 Header 中解析到 args 参数，并使用 JSON 反序列化\n        Demo01Controller.Args args = JSON.parseObject(msg.getHeaders().get(\"args\", String.class),\n                Demo01Controller.Args.class);\n        // ... local transaction process, return rollback, commit or unknown\n        logger.info(\"[executeLocalTransaction][执行本地事务，消息：{} args：{}]\", msg, args);\n        return RocketMQLocalTransactionState.UNKNOWN;\n    }\n\n    @Override\n    public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {\n        // ... check transaction status and return rollback, commit or unknown\n        logger.info(\"[checkLocalTransaction][回查消息：{}]\", msg);\n        return RocketMQLocalTransactionState.COMMIT;\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-transaction/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-transaction/src/main/java/cn/iocoder/springcloudalibaba/labx6/rocketmqdemo/producerdemo/message/MySource.java",
    "content": "package cn.iocoder.springcloudalibaba.labx6.rocketmqdemo.producerdemo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/labx-06-sca-stream-rocketmq-producer-transaction/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n      # Spring Cloud Stream RocketMQ 配置项\n      rocketmq:\n        # RocketMQ Binder 配置项，对应 RocketMQBinderConfigurationProperties 类\n        binder:\n          name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址\n        # RocketMQ 自定义 Binding 配置项，对应 RocketMQBindingProperties Map\n        bindings:\n          demo01-output:\n            # RocketMQ Producer 配置项，对应 RocketMQProducerProperties 类\n            producer:\n              group: test # 生产者分组\n              sync: true # 是否同步发送消息，默认为 false 异步。\n              transactional: true # 是否发送事务消息，默认为 false。\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-06-spring-cloud-stream-rocketmq</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-06-sca-stream-rocketmq-consumer-demo</module>\n        <module>labx-06-sca-stream-rocketmq-producer-demo</module>\n\n        <module>labx-06-sca-stream-rocketmq-consumer-retry</module>\n        <module>labx-06-sca-stream-rocketmq-consumer-error-handler</module>\n\n        <module>labx-06-sca-stream-rocketmq-consumer-broadcasting</module>\n\n        <module>labx-06-sca-stream-rocketmq-producer-orderly</module>\n        <module>labx-06-sca-stream-rocketmq-consumer-orderly</module>\n\n        <module>labx-06-sca-stream-rocketmq-consumer-filter</module>\n\n        <module>labx-06-sca-stream-rocketmq-producer-transaction</module>\n\n        <module>labx-06-sca-stream-rocketmq-producer-actuator</module>\n        <module>labx-06-sca-stream-rocketmq-consumer-actuator</module>\n\n        <module>labx-06-sca-stream-rocketmq-producer-aliyun</module>\n        <module>labx-06-sca-stream-rocketmq-consumer-aliyun</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-06-spring-cloud-stream-rocketmq/《芋道 Spring Cloud Alibaba 消息队列 RocketMQ 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud-Alibaba/RocketMQ/?github>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/labx-07-sca-dubbo-demo01-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-sca-dubbo-demo01</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo01-api</artifactId>\n\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/labx-07-sca-dubbo-demo01-api/src/main/java/cn/iocoder/springcloudalibaba/labx7/api/UserService.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.api;\n\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\n\n/**\n * 用户服务 RPC Service 接口\n */\npublic interface UserService {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    UserDTO get(Integer id);\n\n    /**\n     * 添加新用户，返回新添加的用户编号\n     *\n     * @param addDTO 添加的用户信息\n     * @return 用户编号\n     */\n    Integer add(UserAddDTO addDTO);\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/labx-07-sca-dubbo-demo01-api/src/main/java/cn/iocoder/springcloudalibaba/labx7/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO implements Serializable {\n\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public String getName() {\n        return name;\n    }\n\n    public UserAddDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserAddDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/labx-07-sca-dubbo-demo01-api/src/main/java/cn/iocoder/springcloudalibaba/labx7/dto/UserDTO.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户信息 DTO\n */\npublic class UserDTO implements Serializable {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/labx-07-sca-dubbo-demo01-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-sca-dubbo-demo01</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo01-consumer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-07-sca-dubbo-demo01-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/labx-07-sca-dubbo-demo01-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx7/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.consumerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/labx-07-sca-dubbo-demo01-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx7/consumerdemo/controller/UserController.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.consumerdemo.controller;\n\nimport cn.iocoder.springcloudalibaba.labx7.api.UserService;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Reference(protocol = \"dubbo\", version = \"1.0.0\")\n    private UserService userService;\n\n    @GetMapping(\"/get\")\n    public UserDTO get(@RequestParam(\"id\") Integer id) {\n        return userService.get(id);\n    }\n\n    @PostMapping(\"/add\")\n    public Integer add(UserAddDTO addDTO) {\n        return userService.add(addDTO);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/labx-07-sca-dubbo-demo01-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n# Dubbo 配置项，对应 DubboConfigurationProperties 类\ndubbo:\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: demo-provider # 设置订阅的应用列表，默认为 * 订阅所有应用。\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/labx-07-sca-dubbo-demo01-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-sca-dubbo-demo01</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo01-provider</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-07-sca-dubbo-demo01-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/labx-07-sca-dubbo-demo01-provider/src/main/java/cn/iocoder/springcloudalibaba/labx7/providerdemo/ProviderApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.providerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProviderApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/labx-07-sca-dubbo-demo01-provider/src/main/java/cn/iocoder/springcloudalibaba/labx7/providerdemo/service/UserServiceImpl.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.providerdemo.service;\n\nimport cn.iocoder.springcloudalibaba.labx7.api.UserService;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\n\n@org.apache.dubbo.config.annotation.Service(protocol = \"dubbo\", version = \"1.0.0\")\npublic class UserServiceImpl implements UserService {\n\n    @Override\n    public UserDTO get(Integer id) {\n        return new UserDTO().setId(id)\n                .setName(\"没有昵称：\" + id)\n                .setGender(id % 2 + 1); // 1 - 男；2 - 女\n    }\n\n    @Override\n    public Integer add(UserAddDTO addDTO) {\n        return (int) (System.currentTimeMillis() / 1000); // 嘿嘿，随便返回一个 id\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/labx-07-sca-dubbo-demo01-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\n# Dubbo 配置项，对应 DubboConfigurationProperties 类\ndubbo:\n  scan:\n    base-packages: cn.iocoder.springcloudalibaba.labx7.providerdemo.service # 指定 Dubbo 服务实现类的扫描基准包\n  # Dubbo 服务暴露的协议配置，对应 ProtocolConfig Map\n  protocols:\n    dubbo:\n      name: dubbo # 协议名称\n      port: -1 # 协议端口，-1 表示自增端口，从 20880 开始\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: '' # 设置订阅的应用列表，默认为 * 订阅所有应用。\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-spring-cloud-alibaba-dubbo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo01</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-07-sca-dubbo-demo01-api</module>\n        <module>labx-07-sca-dubbo-demo01-provider</module>\n        <module>labx-07-sca-dubbo-demo01-consumer</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-sca-dubbo-demo02</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo02-api</artifactId>\n\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-api/src/main/java/cn/iocoder/springcloudalibaba/labx7/api/UserService.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.api;\n\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\n\n/**\n * 用户服务 RPC Service 接口\n */\npublic interface UserService {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    UserDTO get(Integer id);\n\n    /**\n     * 添加新用户，返回新添加的用户编号\n     *\n     * @param addDTO 添加的用户信息\n     * @return 用户编号\n     */\n    Integer add(UserAddDTO addDTO);\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-api/src/main/java/cn/iocoder/springcloudalibaba/labx7/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO implements Serializable {\n\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public String getName() {\n        return name;\n    }\n\n    public UserAddDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserAddDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-api/src/main/java/cn/iocoder/springcloudalibaba/labx7/dto/UserDTO.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户信息 DTO\n */\npublic class UserDTO implements Serializable {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-sca-dubbo-demo02</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo02-consumer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-07-sca-dubbo-demo02-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud OpenFeign 相关依赖，使用 OpenFeign 提供声明式调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx7/consumerdemo/FeignConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.consumerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\n\n@SpringBootApplication\n@EnableFeignClients\npublic class FeignConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(FeignConsumerApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx7/consumerdemo/config/RestTemplateConfig.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.consumerdemo.config;\n\nimport com.alibaba.cloud.dubbo.annotation.DubboTransported;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.client.RestTemplate;\n\n@Configuration\npublic class RestTemplateConfig {\n\n    @Bean\n    @LoadBalanced\n    @DubboTransported(protocol = \"dubbo\")\n    public RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx7/consumerdemo/controller/User01Controller.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.consumerdemo.controller;\n\nimport cn.iocoder.springcloudalibaba.labx7.consumerdemo.feign.UserFeignClient;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\n@RequestMapping(\"/user01\")\npublic class User01Controller {\n\n    @Autowired\n    private UserFeignClient userFeignClient;\n\n    @GetMapping(\"/get\")\n    public UserDTO get(@RequestParam(\"id\") Integer id) {\n        return userFeignClient.get(id);\n    }\n\n    @PostMapping(\"/add\")\n    public Integer add(UserAddDTO addDTO) {\n        return userFeignClient.add(addDTO);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx7/consumerdemo/controller/User02Controller.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.consumerdemo.controller;\n\nimport cn.iocoder.springcloudalibaba.labx7.consumerdemo.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.consumerdemo.dto.UserDTO;\nimport cn.iocoder.springcloudalibaba.labx7.consumerdemo.feign.UserFeignClient02;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\n@RequestMapping(\"/user02\")\npublic class User02Controller {\n\n    @Autowired\n    private UserFeignClient02 userFeignClient;\n\n    @GetMapping(\"/get\")\n    public UserDTO get(@RequestParam(\"id\") Integer id) {\n        return userFeignClient.get(id);\n    }\n\n    @PostMapping(\"/add\")\n    public Integer add(UserAddDTO addDTO) {\n        return userFeignClient.add(addDTO);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx7/consumerdemo/controller/User03Controller.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.consumerdemo.controller;\n\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\nimport com.alibaba.fastjson.JSON;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpEntity;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.*;\nimport org.springframework.web.client.RestTemplate;\n\n@RestController\n@RequestMapping(\"/user03\")\npublic class User03Controller {\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @GetMapping(\"/get\")\n    public UserDTO get(@RequestParam(\"id\") Integer id) {\n        String url = String.format(\"http://%s/user/get?id=%d\", \"demo-provider\", id);\n        return restTemplate.getForObject(url, UserDTO.class);\n    }\n\n    @PostMapping(\"/add\")\n    public Integer add(UserAddDTO addDTO) {\n        // 请求头\n        HttpHeaders headers = new HttpHeaders();\n        headers.setContentType(MediaType.APPLICATION_JSON);\n        // 请求体\n        String body = JSON.toJSONString(addDTO);\n        // 创建 HttpEntity 对象\n        HttpEntity<String> entity = new HttpEntity<>(body, headers);\n        // 执行请求\n        String url = String.format(\"http://%s/user/add\", \"demo-provider\");\n        return restTemplate.postForObject(url, entity, Integer.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx7/consumerdemo/controller/User04Controller.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.consumerdemo.controller;\n\nimport cn.iocoder.springcloudalibaba.labx7.api.UserService;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\n@RequestMapping(\"/user04\")\npublic class User04Controller {\n\n    @Reference(version = \"1.0.0\", protocol = \"dubbo\")\n    private UserService userService;\n\n    @GetMapping(\"/get\")\n    public UserDTO get(@RequestParam(\"id\") Integer id) {\n        return userService.get(id);\n    }\n\n    @PostMapping(\"/add\")\n    public Integer add(UserAddDTO addDTO) {\n        return userService.add(addDTO);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx7/consumerdemo/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.consumerdemo.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO implements Serializable {\n\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public String getName() {\n        return name;\n    }\n\n    public UserAddDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserAddDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx7/consumerdemo/dto/UserDTO.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.consumerdemo.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户信息 DTO\n */\npublic class UserDTO implements Serializable {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx7/consumerdemo/feign/UserFeignClient.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.consumerdemo.feign;\n\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\nimport com.alibaba.cloud.dubbo.annotation.DubboTransported;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(name = \"demo-provider\")\n@DubboTransported(protocol = \"dubbo\")\n// @DubboTransported(protocol = \"rest\")\npublic interface UserFeignClient {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    @GetMapping(\"/user/get\")\n    UserDTO get(@RequestParam(\"id\") Integer id);\n\n    /**\n     * 添加新用户，返回新添加的用户编号\n     *\n     * @param addDTO 添加的用户信息\n     * @return 用户编号\n     */\n    @PostMapping(\"/user/add\")\n    Integer add(@RequestBody UserAddDTO addDTO);\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx7/consumerdemo/feign/UserFeignClient02.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.consumerdemo.feign;\n\nimport cn.iocoder.springcloudalibaba.labx7.consumerdemo.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.consumerdemo.dto.UserDTO;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(name = \"demo-provider\")\npublic interface UserFeignClient02 {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    @GetMapping(\"/user/get\")\n    UserDTO get(@RequestParam(\"id\") Integer id);\n\n    /**\n     * 添加新用户，返回新添加的用户编号\n     *\n     * @param addDTO 添加的用户信息\n     * @return 用户编号\n     */\n    @PostMapping(\"/user/add\")\n    Integer add(@RequestBody UserAddDTO addDTO);\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n# Dubbo 配置项，对应 DubboConfigurationProperties 类\ndubbo:\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: demo-provider # 设置订阅的应用列表，默认为 * 订阅所有应用。\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-provider-rest/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-sca-dubbo-demo02</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo02-provider-rest</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n        <dubbo.version>2.7.4.1</dubbo.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n\n            <!-- Dubbo Dependencies -->\n<!--            <dependency>-->\n<!--                <groupId>org.apache.dubbo</groupId>-->\n<!--                <artifactId>dubbo-dependencies-bom</artifactId>-->\n<!--                <version>${dubbo.version}</version>-->\n<!--                <type>pom</type>-->\n<!--                <scope>import</scope>-->\n<!--                <exclusions>-->\n<!--                    <exclusion>-->\n<!--                        <groupId>org.springframework</groupId>-->\n<!--                        <artifactId>spring-framework-bom</artifactId>-->\n<!--                    </exclusion>-->\n<!--                </exclusions>-->\n<!--            </dependency>-->\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-07-sca-dubbo-demo02-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n            <exclusions>\n                <!-- 参考文章 https://stackoverflow.com/questions/48432225/nested-exception-is-java-lang-nosuchmethoderror-javax-ws-rs-clienterrorexceptio -->\n                <exclusion>\n                    <artifactId>jsr311-api</artifactId>\n                    <groupId>javax.ws.rs</groupId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n\n        <!-- 引入 Dubbo Rest 协议相关的依赖 -->\n        <dependency>\n            <groupId>org.jboss.resteasy</groupId>\n            <artifactId>resteasy-netty4</artifactId>\n            <version>3.0.19.Final</version>\n        </dependency>\n        <dependency>\n            <groupId>org.hibernate.validator</groupId>\n            <artifactId>hibernate-validator</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>javax.servlet</groupId>\n            <artifactId>javax.servlet-api</artifactId>\n            <version>3.1.0</version>  <!-- Resolve the Dubbo REST RPC issue -->\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-provider-rest/src/main/java/cn/iocoder/springcloudalibaba/labx7/providerdemo/RestProviderApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.providerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class RestProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(RestProviderApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-provider-rest/src/main/java/cn/iocoder/springcloudalibaba/labx7/providerdemo/service/UserServiceImpl.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.providerdemo.service;\n\nimport cn.iocoder.springcloudalibaba.labx7.api.UserService;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\n\nimport javax.ws.rs.*;\nimport javax.ws.rs.core.MediaType;\n\nimport static org.springframework.util.MimeTypeUtils.APPLICATION_JSON_VALUE;\n\n@org.apache.dubbo.config.annotation.Service(version = \"1.0.0\", protocol = {\"dubbo\", \"rest\"})\n@Path(\"/user\")\npublic class UserServiceImpl implements UserService {\n\n    @Override\n    @GET\n    @Path(\"/get\")\n    @Produces(APPLICATION_JSON_VALUE)\n    public UserDTO get(@QueryParam(\"id\") Integer id) {\n        return new UserDTO().setId(id)\n                .setName(\"没有昵称：\" + id)\n                .setGender(id % 2 + 1); // 1 - 男；2 - 女\n    }\n\n    @Override\n    @POST\n    @Path(\"/add\")\n    @Consumes(MediaType.APPLICATION_JSON)\n    public Integer add(UserAddDTO addDTO) {\n        return (int) (System.currentTimeMillis() / 1000); // 嘿嘿，随便返回一个 id\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-provider-rest/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n  main:\n    web-application-type: NONE # Web 应用类型，这里设置为 NONE\n\n# Dubbo 配置项，对应 DubboConfigurationProperties 类\ndubbo:\n  scan:\n    base-packages: cn.iocoder.springcloudalibaba.labx7.providerdemo.service # 指定 Dubbo 服务实现类的扫描基准包\n  # Dubbo 服务暴露的协议配置，对应 ProtocolConfig Map\n  protocols:\n    dubbo:\n      name: dubbo # 协议名称\n      port: -1 # 协议端口，-1 表示自增端口，从 20880 开始\n    rest:\n      name: rest\n      port: 9090 # 协议端口\n      server: netty # 使用的服务器\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: '' # 设置订阅的应用列表，默认为 * 订阅所有应用。\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-provider-springmvc/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-sca-dubbo-demo02</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo02-provider-springmvc</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n    引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n    在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-07-sca-dubbo-demo01-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n\n        <!-- 引入 Dubbo 拓展的 Actuator  自动化配置 -->\n        <dependency>\n            <groupId>org.apache.dubbo</groupId>\n            <artifactId>dubbo-spring-boot-actuator</artifactId>\n            <version>2.7.4.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-provider-springmvc/src/main/java/cn/iocoder/springcloudalibaba/labx7/providerdemo/SpringMVCProviderApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.providerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class SpringMVCProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SpringMVCProviderApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-provider-springmvc/src/main/java/cn/iocoder/springcloudalibaba/labx7/providerdemo/service/UserServiceImpl.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.providerdemo.service;\n\nimport cn.iocoder.springcloudalibaba.labx7.api.UserService;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\nimport org.springframework.web.bind.annotation.*;\n\n@org.apache.dubbo.config.annotation.Service(version = \"1.0.0\", protocol = {\"dubbo\"})\n@RestController\n@RequestMapping(\"/user\")\npublic class UserServiceImpl implements UserService {\n\n    @Override\n    @GetMapping(\"/get\")\n    public UserDTO get(@RequestParam(\"id\") Integer id) {\n        return new UserDTO().setId(id)\n                .setName(\"没有昵称：\" + id)\n                .setGender(id % 2 + 1); // 1 - 男；2 - 女\n    }\n\n    @Override\n    @PostMapping(\"/add\")\n    public Integer add(@RequestBody UserAddDTO addDTO) {\n        return (int) (System.currentTimeMillis() / 1000); // 嘿嘿，随便返回一个 id\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/labx-07-sca-dubbo-demo02-provider-springmvc/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: 18080 # 服务器端口，默认为 8080\n\n# Dubbo 配置项，对应 DubboConfigurationProperties 类\ndubbo:\n  scan:\n    base-packages: cn.iocoder.springcloudalibaba.labx7.providerdemo.service # 指定 Dubbo 服务实现类的扫描基准包\n  # Dubbo 服务暴露的协议配置，对应 ProtocolConfig Map\n  protocols:\n    dubbo:\n      name: dubbo # 协议名称\n      port: -1 # 协议端口，-1 表示自增端口，从 20880 开始\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: '' # 设置订阅的应用列表，默认为 * 订阅所有应用。\n\nmanagement:\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n  endpoint:\n    # Health 端点配置项，对应 HealthProperties 配置类\n    health:\n      enabled: true # 是否开启。默认为 true 开启。\n      show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户；可选 ALWAYS 总是展示。\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo02/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-spring-cloud-alibaba-dubbo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo02</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-07-sca-dubbo-demo02-api</module>\n\n        <module>labx-07-sca-dubbo-demo02-provider-rest</module>\n        <module>labx-07-sca-dubbo-demo02-provider-springmvc</module>\n        <module>labx-07-sca-dubbo-demo02-consumer</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/labx-07-sca-dubbo-demo03-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-sca-dubbo-demo03-validation</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo03-api</artifactId>\n\n    <dependencies>\n        <!-- 参数校验相关依赖 -->\n        <dependency>\n            <groupId>javax.validation</groupId>\n            <artifactId>validation-api</artifactId> <!-- JSR 参数校验规范 API -->\n            <version>2.0.1.Final</version>\n        </dependency>\n        <dependency>\n            <groupId>org.hibernate.validator</groupId>\n            <artifactId>hibernate-validator</artifactId> <!-- JSR 参数校验规范实现，我们使用 hibernate-validator -->\n            <version>6.0.18.Final</version>\n        </dependency>\n        <dependency>\n            <groupId>org.glassfish</groupId>\n            <artifactId>javax.el</artifactId> <!-- 可能涉及到 EL 表达，所以引入，否则 hibernate-validator 在初始化会报错 -->\n            <version>3.0.1-b11</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/labx-07-sca-dubbo-demo03-api/src/main/java/cn/iocoder/springcloudalibaba/labx7/api/UserService.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.api;\n\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\n\nimport javax.validation.ConstraintViolationException;\nimport javax.validation.constraints.NotNull;\n\n/**\n * 用户服务 RPC Service 接口\n */\npublic interface UserService {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    UserDTO get(@NotNull(message = \"用户编号不能为空\") Integer id)\n            throws ConstraintViolationException;;\n\n    /**\n     * 添加新用户，返回新添加的用户编号\n     *\n     * @param addDTO 添加的用户信息\n     * @return 用户编号\n     */\n    Integer add(UserAddDTO addDTO)\n            throws ConstraintViolationException;;\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/labx-07-sca-dubbo-demo03-api/src/main/java/cn/iocoder/springcloudalibaba/labx7/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.dto;\n\nimport org.hibernate.validator.constraints.Length;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO implements Serializable {\n\n    /**\n     * 昵称\n     */\n    @NotEmpty(message = \"昵称不能为空\")\n    @Length(min = 5, max = 16, message = \"账号长度为 5-16 位\")\n    private String name;\n    /**\n     * 性别\n     */\n    @NotNull(message = \"性别不能为空\")\n    private Integer gender;\n\n    public String getName() {\n        return name;\n    }\n\n    public UserAddDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserAddDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/labx-07-sca-dubbo-demo03-api/src/main/java/cn/iocoder/springcloudalibaba/labx7/dto/UserDTO.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户信息 DTO\n */\npublic class UserDTO implements Serializable {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/labx-07-sca-dubbo-demo03-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-sca-dubbo-demo03-validation</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo03-consumer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n    引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n    在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-07-sca-dubbo-demo03-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/labx-07-sca-dubbo-demo03-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx7/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.consumerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/labx-07-sca-dubbo-demo03-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx7/consumerdemo/controller/UserController.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.consumerdemo.controller;\n\nimport cn.iocoder.springcloudalibaba.labx7.api.UserService;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Reference(protocol = \"dubbo\", version = \"1.0.0\", validation = \"true\")\n    private UserService userService;\n\n    @GetMapping(\"/get\")\n    public UserDTO get(@RequestParam(\"id\") Integer id) {\n        return userService.get(id);\n    }\n\n    @PostMapping(\"/add\")\n    public Integer add(UserAddDTO addDTO) {\n        return userService.add(addDTO);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/labx-07-sca-dubbo-demo03-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n# Dubbo 配置项，对应 DubboConfigurationProperties 类\ndubbo:\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: demo-provider # 设置订阅的应用列表，默认为 * 订阅所有应用。\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/labx-07-sca-dubbo-demo03-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-sca-dubbo-demo03-validation</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo03-provider</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n    引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n    在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-07-sca-dubbo-demo03-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/labx-07-sca-dubbo-demo03-provider/src/main/java/cn/iocoder/springcloudalibaba/labx7/providerdemo/ProviderApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.providerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProviderApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/labx-07-sca-dubbo-demo03-provider/src/main/java/cn/iocoder/springcloudalibaba/labx7/providerdemo/service/UserServiceImpl.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.providerdemo.service;\n\nimport cn.iocoder.springcloudalibaba.labx7.api.UserService;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\n\n@org.apache.dubbo.config.annotation.Service(protocol = \"dubbo\", version = \"1.0.0\", validation = \"true\")\npublic class UserServiceImpl implements UserService {\n\n    @Override\n    public UserDTO get(Integer id) {\n        return new UserDTO().setId(id)\n                .setName(\"没有昵称：\" + id)\n                .setGender(id % 2 + 1); // 1 - 男；2 - 女\n    }\n\n    @Override\n    public Integer add(UserAddDTO addDTO) {\n        return (int) (System.currentTimeMillis() / 1000); // 嘿嘿，随便返回一个 id\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/labx-07-sca-dubbo-demo03-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\n# Dubbo 配置项，对应 DubboConfigurationProperties 类\ndubbo:\n  scan:\n    base-packages: cn.iocoder.springcloudalibaba.labx7.providerdemo.service # 指定 Dubbo 服务实现类的扫描基准包\n  # Dubbo 服务暴露的协议配置，对应 ProtocolConfig Map\n  protocols:\n    dubbo:\n      name: dubbo # 协议名称\n      port: -1 # 协议端口，-1 表示自增端口，从 20880 开始\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: '' # 设置订阅的应用列表，默认为 * 订阅所有应用。\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo03-validation/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-spring-cloud-alibaba-dubbo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo03-validation</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-07-sca-dubbo-demo03-api</module>\n        <module>labx-07-sca-dubbo-demo03-provider</module>\n        <module>labx-07-sca-dubbo-demo03-consumer</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-sca-dubbo-demo04-filter</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo04-api</artifactId>\n\n    <dependencies>\n        <!-- 参数校验相关依赖 -->\n        <dependency>\n            <groupId>javax.validation</groupId>\n            <artifactId>validation-api</artifactId> <!-- JSR 参数校验规范 API -->\n            <version>2.0.1.Final</version>\n        </dependency>\n        <dependency>\n            <groupId>org.hibernate.validator</groupId>\n            <artifactId>hibernate-validator</artifactId> <!-- JSR 参数校验规范实现，我们使用 hibernate-validator -->\n            <version>6.0.18.Final</version>\n        </dependency>\n        <dependency>\n            <groupId>org.glassfish</groupId>\n            <artifactId>javax.el</artifactId> <!-- 可能涉及到 EL 表达，所以引入，否则 hibernate-validator 在初始化会报错 -->\n            <version>3.0.1-b11</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-api/src/main/java/cn/iocoder/springcloudalibaba/labx7/api/UserService.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.api;\n\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\n\nimport javax.validation.ConstraintViolationException;\nimport javax.validation.constraints.NotNull;\n\n/**\n * 用户服务 RPC Service 接口\n */\npublic interface UserService {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    UserDTO get(@NotNull(message = \"用户编号不能为空\") Integer id)\n            throws ConstraintViolationException;;\n\n    /**\n     * 添加新用户，返回新添加的用户编号\n     *\n     * @param addDTO 添加的用户信息\n     * @return 用户编号\n     */\n    Integer add(UserAddDTO addDTO)\n            throws ConstraintViolationException;;\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-api/src/main/java/cn/iocoder/springcloudalibaba/labx7/core/ServiceException.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.core;\n\n/**\n * 服务异常\n *\n * 参考 https://www.kancloud.cn/onebase/ob/484204 文章\n *\n * 一共 10 位，分成四段\n *\n * 第一段，1 位，类型\n *      1 - 业务级别异常\n *      2 - 系统级别异常\n * 第二段，3 位，系统类型\n *      001 - 用户系统\n *      002 - 商品系统\n *      003 - 订单系统\n *      004 - 支付系统\n *      005 - 优惠劵系统\n *      ... - ...\n * 第三段，3 位，模块\n *      不限制规则。\n *      一般建议，每个系统里面，可能有多个模块，可以再去做分段。以用户系统为例子：\n *          001 - OAuth2 模块\n *          002 - User 模块\n *          003 - MobileCode 模块\n * 第四段，3 位，错误码\n *       不限制规则。\n *       一般建议，每个模块自增。\n */\npublic final class ServiceException extends RuntimeException {\n\n    /**\n     * 错误码\n     */\n    private Integer code;\n\n    public ServiceException() { // 创建默认构造方法，用于反序列化的场景。\n    }\n\n    public ServiceException(ServiceExceptionEnum serviceExceptionEnum) {\n        // 使用父类的 message 字段\n        super(serviceExceptionEnum.getMessage());\n        // 设置错误码\n        this.code = serviceExceptionEnum.getCode();\n    }\n\n    public ServiceException(ServiceExceptionEnum serviceExceptionEnum, String message) {\n        // 使用父类的 message 字段\n        super(message);\n        // 设置错误码\n        this.code = serviceExceptionEnum.getCode();\n    }\n\n    public Integer getCode() {\n        return code;\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-api/src/main/java/cn/iocoder/springcloudalibaba/labx7/core/ServiceExceptionEnum.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.core;\n\n/**\n * 业务异常枚举\n */\npublic enum ServiceExceptionEnum {\n\n    // ========== 系统级别 ==========\n    SUCCESS(0, \"成功\"),\n    SYS_ERROR(2001001000, \"服务端发生异常\"),\n    MISSING_REQUEST_PARAM_ERROR(2001001001, \"参数缺失\"),\n    INVALID_REQUEST_PARAM_ERROR(2001001002, \"请求参数不合法\"),\n\n    // ========== 用户模块 ==========\n    USER_NOT_FOUND(1001002000, \"用户不存在\"),\n    USER_EXISTS(1001002001, \"用户已存在\"),\n\n    // ========== 订单模块 ==========\n\n    // ========== 商品模块 ==========\n    ;\n\n    /**\n     * 错误码\n     */\n    private final int code;\n    /**\n     * 错误提示\n     */\n    private final String message;\n\n    ServiceExceptionEnum(int code, String message) {\n        this.code = code;\n        this.message = message;\n    }\n\n    public int getCode() {\n        return code;\n    }\n\n    public String getMessage() {\n        return message;\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-api/src/main/java/cn/iocoder/springcloudalibaba/labx7/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.dto;\n\nimport org.hibernate.validator.constraints.Length;\n\nimport javax.validation.constraints.NotEmpty;\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO implements Serializable {\n\n    /**\n     * 昵称\n     */\n    @NotEmpty(message = \"昵称不能为空\")\n    @Length(min = 5, max = 16, message = \"账号长度为 5-16 位\")\n    private String name;\n    /**\n     * 性别\n     */\n    @NotNull(message = \"性别不能为空\")\n    private Integer gender;\n\n    public String getName() {\n        return name;\n    }\n\n    public UserAddDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserAddDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-api/src/main/java/cn/iocoder/springcloudalibaba/labx7/dto/UserDTO.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户信息 DTO\n */\npublic class UserDTO implements Serializable {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-sca-dubbo-demo04-filter</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo04-consumer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n    引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n    在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-07-sca-dubbo-demo04-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx7/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.consumerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx7/consumerdemo/controller/UserController.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.consumerdemo.controller;\n\nimport cn.iocoder.springcloudalibaba.labx7.api.UserService;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Reference(protocol = \"dubbo\", version = \"1.0.0\", validation = \"true\")\n    private UserService userService;\n\n    @GetMapping(\"/get\")\n    public UserDTO get(@RequestParam(\"id\") Integer id) {\n        return userService.get(id);\n    }\n\n    @PostMapping(\"/add\")\n    public Integer add(UserAddDTO addDTO) {\n        return userService.add(addDTO);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n# Dubbo 配置项，对应 DubboConfigurationProperties 类\ndubbo:\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: demo-provider # 设置订阅的应用列表，默认为 * 订阅所有应用。\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-sca-dubbo-demo04-filter</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo04-provider</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n    引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n    在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-07-sca-dubbo-demo04-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-provider/src/main/java/cn/iocoder/springcloudalibaba/labx7/providerdemo/ProviderApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.providerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProviderApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-provider/src/main/java/cn/iocoder/springcloudalibaba/labx7/providerdemo/filter/DubboExceptionFilter.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.providerdemo.filter;\n\nimport cn.iocoder.springcloudalibaba.labx7.core.ServiceException;\nimport cn.iocoder.springcloudalibaba.labx7.core.ServiceExceptionEnum;\nimport org.apache.dubbo.common.constants.CommonConstants;\nimport org.apache.dubbo.common.extension.Activate;\nimport org.apache.dubbo.common.logger.Logger;\nimport org.apache.dubbo.common.logger.LoggerFactory;\nimport org.apache.dubbo.common.utils.ReflectUtils;\nimport org.apache.dubbo.common.utils.StringUtils;\nimport org.apache.dubbo.rpc.*;\nimport org.apache.dubbo.rpc.service.GenericService;\n\nimport javax.validation.ConstraintViolation;\nimport javax.validation.ConstraintViolationException;\nimport java.lang.reflect.Method;\n\n@Activate(group = CommonConstants.PROVIDER)\npublic class DubboExceptionFilter extends ListenableFilter {\n\n    public DubboExceptionFilter() {\n        super.listener = new ExceptionListenerX();\n    }\n\n    @Override\n    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {\n        return invoker.invoke(invocation);\n    }\n\n    static class ExceptionListenerX extends ExceptionListener {\n\n        @Override\n        public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {\n            // 发生异常，并且非泛化调用\n            if (appResponse.hasException() && GenericService.class != invoker.getInterface()) {\n                Throwable exception = appResponse.getException();\n                // <1> 如果是 ServiceException 异常，直接返回\n                if (exception instanceof ServiceException) {\n                    return;\n                }\n                // <2> 如果是参数校验的 ConstraintViolationException 异常，则封装返回\n                if (exception instanceof ConstraintViolationException) {\n                    appResponse.setException(this.handleConstraintViolationException((ConstraintViolationException) exception));\n                    return;\n                }\n            }\n            // <3> 其它情况，继续使用父类处理\n            super.onResponse(appResponse, invoker, invocation);\n        }\n\n        // 将 ConstraintViolationException 转换成 ServiceException\n        private ServiceException handleConstraintViolationException(ConstraintViolationException ex) {\n            // 拼接错误\n            StringBuilder detailMessage = new StringBuilder();\n            for (ConstraintViolation<?> constraintViolation : ex.getConstraintViolations()) {\n                // 使用 ; 分隔多个错误\n                if (detailMessage.length() > 0) {\n                    detailMessage.append(\";\");\n                }\n                // 拼接内容到其中\n                detailMessage.append(constraintViolation.getMessage());\n            }\n            // 返回异常\n            return new ServiceException(ServiceExceptionEnum.INVALID_REQUEST_PARAM_ERROR, detailMessage.toString());\n        }\n\n    }\n\n    static class ExceptionListener implements Listener {\n\n        private Logger logger = LoggerFactory.getLogger(ExceptionListener.class);\n\n        @Override\n        public void onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {\n            if (appResponse.hasException() && GenericService.class != invoker.getInterface()) {\n                try {\n                    Throwable exception = appResponse.getException();\n\n                    // directly throw if it's checked exception\n                    if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) {\n                        return;\n                    }\n                    // directly throw if the exception appears in the signature\n                    try {\n                        Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());\n                        Class<?>[] exceptionClassses = method.getExceptionTypes();\n                        for (Class<?> exceptionClass : exceptionClassses) {\n                            if (exception.getClass().equals(exceptionClass)) {\n                                return;\n                            }\n                        }\n                    } catch (NoSuchMethodException e) {\n                        return;\n                    }\n\n                    // for the exception not found in method's signature, print ERROR message in server's log.\n                    logger.error(\"Got unchecked and undeclared exception which called by \" + RpcContext.getContext().getRemoteHost() + \". service: \" + invoker.getInterface().getName() + \", method: \" + invocation.getMethodName() + \", exception: \" + exception.getClass().getName() + \": \" + exception.getMessage(), exception);\n\n                    // directly throw if exception class and interface class are in the same jar file.\n                    String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());\n                    String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());\n                    if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) {\n                        return;\n                    }\n                    // directly throw if it's JDK exception\n                    String className = exception.getClass().getName();\n                    if (className.startsWith(\"java.\") || className.startsWith(\"javax.\")) {\n                        return;\n                    }\n                    // directly throw if it's dubbo exception\n                    if (exception instanceof RpcException) {\n                        return;\n                    }\n\n                    // otherwise, wrap with RuntimeException and throw back to the client\n                    appResponse.setException(new RuntimeException(StringUtils.toString(exception)));\n                    return;\n                } catch (Throwable e) {\n                    logger.warn(\"Fail to ExceptionFilter when called by \" + RpcContext.getContext().getRemoteHost() + \". service: \" + invoker.getInterface().getName() + \", method: \" + invocation.getMethodName() + \", exception: \" + e.getClass().getName() + \": \" + e.getMessage(), e);\n                    return;\n                }\n            }\n        }\n\n        @Override\n        public void onError(Throwable e, Invoker<?> invoker, Invocation invocation) {\n            logger.error(\"Got unchecked and undeclared exception which called by \" + RpcContext.getContext().getRemoteHost() + \". service: \" + invoker.getInterface().getName() + \", method: \" + invocation.getMethodName() + \", exception: \" + e.getClass().getName() + \": \" + e.getMessage(), e);\n        }\n\n        // For test purpose\n        public void setLogger(Logger logger) {\n            this.logger = logger;\n        }\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-provider/src/main/java/cn/iocoder/springcloudalibaba/labx7/providerdemo/service/UserServiceImpl.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.providerdemo.service;\n\nimport cn.iocoder.springcloudalibaba.labx7.api.UserService;\nimport cn.iocoder.springcloudalibaba.labx7.core.ServiceException;\nimport cn.iocoder.springcloudalibaba.labx7.core.ServiceExceptionEnum;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\n\n@org.apache.dubbo.config.annotation.Service(protocol = \"dubbo\", version = \"1.0.0\", validation = \"true\", filter = \"-exception\")\npublic class UserServiceImpl implements UserService {\n\n    @Override\n    public UserDTO get(Integer id) {\n        return new UserDTO().setId(id)\n                .setName(\"没有昵称：\" + id)\n                .setGender(id % 2 + 1); // 1 - 男；2 - 女\n    }\n\n    @Override\n    public Integer add(UserAddDTO addDTO) {\n        // 【额外添加】这里，模拟用户已经存在的情况\n        if (\"yudaoyuanma\".equals(addDTO.getName())) {\n            throw new ServiceException(ServiceExceptionEnum.USER_EXISTS);\n        }\n        return (int) (System.currentTimeMillis() / 1000); // 嘿嘿，随便返回一个 id\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-provider/src/main/resources/META-INF/dubbo/org.apache.dubbo.rpc.Filter",
    "content": "dubboExceptionFilter=cn.iocoder.springcloudalibaba.labx7.providerdemo.filter.DubboExceptionFilter\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/labx-07-sca-dubbo-demo04-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\n# Dubbo 配置项，对应 DubboConfigurationProperties 类\ndubbo:\n  scan:\n    base-packages: cn.iocoder.springcloudalibaba.labx7.providerdemo.service # 指定 Dubbo 服务实现类的扫描基准包\n  # Dubbo 服务暴露的协议配置，对应 ProtocolConfig Map\n  protocols:\n    dubbo:\n      name: dubbo # 协议名称\n      port: -1 # 协议端口，-1 表示自增端口，从 20880 开始\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: '' # 设置订阅的应用列表，默认为 * 订阅所有应用。\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo04-filter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-spring-cloud-alibaba-dubbo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo04-filter</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-07-sca-dubbo-demo04-api</module>\n        <module>labx-07-sca-dubbo-demo04-provider</module>\n        <module>labx-07-sca-dubbo-demo04-consumer</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/labx-07-sca-dubbo-demo05-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-sca-dubbo-demo05-sentinel</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo05-api</artifactId>\n\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/labx-07-sca-dubbo-demo05-api/src/main/java/cn/iocoder/springcloudalibaba/labx7/api/UserService.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.api;\n\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\n\n/**\n * 用户服务 RPC Service 接口\n */\npublic interface UserService {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    UserDTO get(Integer id);\n\n    /**\n     * 添加新用户，返回新添加的用户编号\n     *\n     * @param addDTO 添加的用户信息\n     * @return 用户编号\n     */\n    Integer add(UserAddDTO addDTO);\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/labx-07-sca-dubbo-demo05-api/src/main/java/cn/iocoder/springcloudalibaba/labx7/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO implements Serializable {\n\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public String getName() {\n        return name;\n    }\n\n    public UserAddDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserAddDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/labx-07-sca-dubbo-demo05-api/src/main/java/cn/iocoder/springcloudalibaba/labx7/dto/UserDTO.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户信息 DTO\n */\npublic class UserDTO implements Serializable {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/labx-07-sca-dubbo-demo05-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-sca-dubbo-demo05-sentinel</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo05-consumer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n    引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n    在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-07-sca-dubbo-demo01-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Sentinel 相关依赖，使用 Sentinel 提供服务保障，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-apache-dubbo-adapter</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/labx-07-sca-dubbo-demo05-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx7/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.consumerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/labx-07-sca-dubbo-demo05-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx7/consumerdemo/controller/UserController.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.consumerdemo.controller;\n\nimport cn.iocoder.springcloudalibaba.labx7.api.UserService;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Reference(protocol = \"dubbo\", version = \"1.0.0\")\n    private UserService userService;\n\n    @GetMapping(\"/get\")\n    public UserDTO get(@RequestParam(\"id\") Integer id) {\n        return userService.get(id);\n    }\n\n    @PostMapping(\"/add\")\n    public Integer add(UserAddDTO addDTO) {\n        return userService.add(addDTO);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/labx-07-sca-dubbo-demo05-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n    # Sentinel 配置项\n    sentinel:\n      eager: true # 是否饥饿加载。默认为 false 关闭\n      transport:\n        dashboard: 127.0.0.1:7070 # Sentinel 控制台地址\n\n# Dubbo 配置项，对应 DubboConfigurationProperties 类\ndubbo:\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: demo-provider # 设置订阅的应用列表，默认为 * 订阅所有应用。\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/labx-07-sca-dubbo-demo05-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-sca-dubbo-demo05-sentinel</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo05-provider</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n    引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n    在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-07-sca-dubbo-demo01-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Sentinel 相关依赖，使用 Sentinel 提供服务保障，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.csp</groupId>\n            <artifactId>sentinel-apache-dubbo-adapter</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/labx-07-sca-dubbo-demo05-provider/src/main/java/cn/iocoder/springcloudalibaba/labx7/providerdemo/ProviderApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.providerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProviderApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/labx-07-sca-dubbo-demo05-provider/src/main/java/cn/iocoder/springcloudalibaba/labx7/providerdemo/service/UserServiceImpl.java",
    "content": "package cn.iocoder.springcloudalibaba.labx7.providerdemo.service;\n\nimport cn.iocoder.springcloudalibaba.labx7.api.UserService;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserAddDTO;\nimport cn.iocoder.springcloudalibaba.labx7.dto.UserDTO;\n\n@org.apache.dubbo.config.annotation.Service(protocol = \"dubbo\", version = \"1.0.0\")\npublic class UserServiceImpl implements UserService {\n\n    @Override\n    public UserDTO get(Integer id) {\n        return new UserDTO().setId(id)\n                .setName(\"没有昵称：\" + id)\n                .setGender(id % 2 + 1); // 1 - 男；2 - 女\n    }\n\n    @Override\n    public Integer add(UserAddDTO addDTO) {\n        return (int) (System.currentTimeMillis() / 1000); // 嘿嘿，随便返回一个 id\n    }\n\n}\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/labx-07-sca-dubbo-demo05-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n    # Sentinel 配置项\n    sentinel:\n      eager: true # 是否饥饿加载。默认为 false 关闭\n      transport:\n        dashboard: 127.0.0.1:7070 # Sentinel 控制台地址\n\n# Dubbo 配置项，对应 DubboConfigurationProperties 类\ndubbo:\n  scan:\n    base-packages: cn.iocoder.springcloudalibaba.labx7.providerdemo.service # 指定 Dubbo 服务实现类的扫描基准包\n  # Dubbo 服务暴露的协议配置，对应 ProtocolConfig Map\n  protocols:\n    dubbo:\n      name: dubbo # 协议名称\n      port: -1 # 协议端口，-1 表示自增端口，从 20880 开始\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: '' # 设置订阅的应用列表，默认为 * 订阅所有应用。\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/labx-07-sca-dubbo-demo05-sentinel/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-07-spring-cloud-alibaba-dubbo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-sca-dubbo-demo05-sentinel</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-07-sca-dubbo-demo05-api</module>\n        <module>labx-07-sca-dubbo-demo05-provider</module>\n        <module>labx-07-sca-dubbo-demo05-consumer</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-07-spring-cloud-alibaba-dubbo</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-07-sca-dubbo-demo01</module>\n        <module>labx-07-sca-dubbo-demo02</module>\n        <module>labx-07-sca-dubbo-demo03-validation</module>\n        <module>labx-07-sca-dubbo-demo04-filter</module>\n        <module>labx-07-sca-dubbo-demo05-sentinel</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-07-spring-cloud-alibaba-dubbo/《芋道 Spring Cloud Alibaba 服务调用 Dubbo 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud-Alibaba/Dubbo/?github>\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-08-spring-cloud-gateway</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-08-sc-gateway-demo01</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Gateway 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-gateway</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo01/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class GatewayApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(GatewayApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo01/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: gateway-application\n\n  cloud:\n    # Spring Cloud Gateway 配置项，对应 GatewayProperties 类\n    gateway:\n      # 路由配置项，对应 RouteDefinition 数组\n      routes:\n        - id: yudaoyuanma # 路由的编号\n          uri: http://www.iocoder.cn # 路由到的目标地址\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/blog\n          filters:\n            - StripPrefix=1\n        - id: oschina # 路由的编号\n          uri: https://www.oschina.net # 路由的目标地址\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/oschina\n          filters: # 过滤器，对请求进行拦截，实现自定义的功能，对应 FilterDefinition 数组\n            - StripPrefix=1\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo01-test/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-08-spring-cloud-gateway</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-08-sc-gateway-demo01-test</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Gateway 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-gateway</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo01-test/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class GatewayApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(GatewayApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo01-test/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/config/GatewayConfig.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo.config;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.gateway.filter.GlobalFilter;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.core.annotation.Order;\nimport reactor.core.publisher.Mono;\n\n//@Configuration\npublic class GatewayConfig {\n\n    private Logger logger = LoggerFactory.getLogger(GatewayConfig.class);\n\n    @Bean\n    @Order(1)\n    public GlobalFilter firstGlobalFilter() {\n        return (exchange, chain) -> {\n            logger.info(\"[first][pre]\");\n            return chain.filter(exchange)\n                    .then(Mono.<Void>fromRunnable(() -> logger.info(\"[first][post]\")));\n        };\n    }\n\n    @Bean\n    @Order(2)\n    public GlobalFilter secondGatewayFilter() {\n        return (exchange, chain) -> {\n            logger.info(\"[second][pre]\");\n            return chain.filter(exchange)\n                    .then(Mono.<Void>fromRunnable(() -> logger.info(\"[second][post]\")));\n        };\n    }\n\n    @Bean\n    @Order(3)\n    public GlobalFilter thirdGlobalFilter() {\n        return (exchange, chain) -> {\n            logger.info(\"[third][pre]\");\n            return chain.filter(exchange)\n                    .then(Mono.<Void>fromRunnable(() -> logger.info(\"[third][post]\")));\n        };\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo01-test/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: gateway-application\n\n  cloud:\n    # Spring Cloud Gateway 配置项，对应 GatewayProperties 类\n    gateway:\n      # 路由配置项，对应 RouteDefinition 数组\n      routes:\n        - id: yudaoyuanma # 路由的编号\n          uri: http://www.iocoder.cn # 路由到的目标地址\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/blog\n          filters:\n            - StripPrefix=1\n        - id: oschina # 路由的编号\n          uri: https://www.oschina.net # 路由的目标地址\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/oschina\n          filters: # 过滤器，对请求进行拦截，实现自定义的功能，对应 FilterDefinition 数组\n            - StripPrefix=100\n#            - StripPrefix=200\n#      default-filters:\n#        - StripPrefix=1\n#        - StripPrefix=2\n#        - StripPrefix=3\n\n#      httpserver:\n#        wiretap: true\n#      httpclient:\n#        wiretap: true\n\nlogging:\n  level:\n    reactor.netty: DEBUG\n    org.springframework.cloud.gateway: TRACE\n#    org.springframework.web.reactive: TRACE\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo02-registry/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-08-spring-cloud-gateway</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-08-sc-gateway-demo02-registry</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Gateway 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-gateway</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo02-registry/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class GatewayApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(GatewayApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo02-registry/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: gateway-application\n\n  cloud:\n    # Spring Cloud Gateway 配置项，对应 GatewayProperties 类\n    gateway:\n      # 路由配置项，对应 RouteDefinition 数组\n      routes:\n        - id: yudaoyuanma # 路由的编号\n          uri: http://www.iocoder.cn # 路由到的目标地址\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/blog\n          filters:\n            - StripPrefix=1\n        - id: oschina # 路由的编号\n          uri: https://www.oschina.net # 路由的目标地址\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/oschina\n          filters: # 过滤器，对请求进行拦截，实现自定义的功能，对应 FilterDefinition 数组\n            - StripPrefix=1\n      # 与 Spring Cloud 注册中心的集成，对应 DiscoveryLocatorProperties 类\n      discovery:\n        locator:\n          enabled: true # 是否开启，默认为 false 关闭\n          url-expression: \"'lb://' + serviceId\" # 路由的目标地址的表达式，默认为 \"'lb://' + serviceId\"\n\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo03-config-apollo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-08-spring-cloud-gateway</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-08-sc-gateway-demo03-config-apollo</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Gateway 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-gateway</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!--  引入 Apollo 客户端，内置对 Apollo 的自动化配置 -->\n        <dependency>\n            <groupId>com.ctrip.framework.apollo</groupId>\n            <artifactId>apollo-client</artifactId>\n            <version>1.5.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo03-config-apollo/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class GatewayApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(GatewayApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo03-config-apollo/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayPropertiesRefresher.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo;\n\nimport com.ctrip.framework.apollo.enums.PropertyChangeType;\nimport com.ctrip.framework.apollo.model.ConfigChange;\nimport com.ctrip.framework.apollo.model.ConfigChangeEvent;\nimport com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.BeansException;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.context.environment.EnvironmentChangeEvent;\nimport org.springframework.cloud.gateway.config.GatewayProperties;\nimport org.springframework.cloud.gateway.event.RefreshRoutesEvent;\nimport org.springframework.cloud.gateway.route.RouteDefinitionWriter;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.ApplicationEventPublisherAware;\nimport org.springframework.stereotype.Component;\n\nimport java.util.ArrayList;\n\n/**\n * 由 https://github.com/ctripcorp/apollo-use-cases/tree/master/spring-cloud-gateway 提供代码，感谢~\n */\n@Component\npublic class GatewayPropertiesRefresher implements ApplicationContextAware, ApplicationEventPublisherAware {\n\n    private static final Logger logger = LoggerFactory.getLogger(GatewayPropertiesRefresher.class);\n\n    private static final String ID_PATTERN = \"spring\\\\.cloud\\\\.gateway\\\\.routes\\\\[\\\\d+\\\\]\\\\.id\";\n\n    private static final String DEFAULT_FILTER_PATTERN = \"spring\\\\.cloud\\\\.gateway\\\\.default-filters\\\\[\\\\d+\\\\]\\\\.name\";\n\n    private ApplicationContext applicationContext;\n\n    private ApplicationEventPublisher publisher;\n\n    @Autowired\n    private GatewayProperties gatewayProperties;\n\n    @Autowired\n    private RouteDefinitionWriter routeDefinitionWriter;\n\n    @Override\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n        this.applicationContext = applicationContext;\n    }\n\n    @Override\n    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {\n        this.publisher = applicationEventPublisher;\n    }\n\n    @ApolloConfigChangeListener(interestedKeyPrefixes = \"spring.cloud.gateway.\")\n    public void onChange(ConfigChangeEvent changeEvent) {\n        refreshGatewayProperties(changeEvent);\n    }\n\n    /***\n     * 刷新org.springframework.cloud.gateway.config.PropertiesRouteDefinitionLocator中定义的routes\n     *\n     * @param changeEvent\n     * @return void\n     * @author ksewen\n     * @date 2019/5/21 2:13 PM\n     */\n    private void refreshGatewayProperties(ConfigChangeEvent changeEvent) {\n        logger.info(\"Refreshing GatewayProperties!\");\n        // <1>\n        preDestroyGatewayProperties(changeEvent);\n        // <2>\n        this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys()));\n        // <3>\n        refreshGatewayRouteDefinition();\n        logger.info(\"GatewayProperties refreshed!\");\n    }\n\n    /***\n     * GatewayProperties没有@PreDestroy和destroy方法\n     * org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder#rebind(java.lang.String)中destroyBean时不会销毁当前对象\n     * 如果把spring.cloud.gateway.前缀的配置项全部删除（例如需要动态删除最后一个路由的场景），initializeBean时也无法创建新的bean，则return当前bean\n     * 若仍保留有spring.cloud.gateway.routes[n]或spring.cloud.gateway.default-filters[n]等配置，initializeBean时会注入新的属性替换已有的bean\n     * 这个方法提供了类似@PreDestroy的操作，根据配置文件的实际情况把org.springframework.cloud.gateway.config.GatewayProperties#routes\n     * 和org.springframework.cloud.gateway.config.GatewayProperties#defaultFilters两个集合清空\n     *\n     * @param\n     * @return void\n     * @author ksewen\n     * @date 2019/5/21 2:13 PM\n     */\n    private synchronized void preDestroyGatewayProperties(ConfigChangeEvent changeEvent) {\n        logger.info(\"Pre Destroy GatewayProperties!\");\n        // 判断 `spring.cloud.gateway.routes` 配置项，是否被全部删除。如果是，则置空 GatewayProperties 的 `routes` 属性\n        final boolean needClearRoutes = this.checkNeedClear(changeEvent, ID_PATTERN, this.gatewayProperties.getRoutes().size());\n        if (needClearRoutes) {\n            this.gatewayProperties.setRoutes(new ArrayList<>());\n        }\n        // 判断 `spring.cloud.gateway.default-filters` 配置项，是否被全部删除。如果是，则置空 GatewayProperties 的 `defaultFilters` 属性\n        final boolean needClearDefaultFilters = this.checkNeedClear(changeEvent, DEFAULT_FILTER_PATTERN, this.gatewayProperties.getDefaultFilters().size());\n        if (needClearDefaultFilters) {\n            this.gatewayProperties.setRoutes(new ArrayList<>());\n        }\n        logger.info(\"Pre Destroy GatewayProperties finished!\");\n    }\n\n    private void refreshGatewayRouteDefinition() {\n        logger.info(\"Refreshing Gateway RouteDefinition!\");\n        this.publisher.publishEvent(new RefreshRoutesEvent(this));\n        logger.info(\"Gateway RouteDefinition refreshed!\");\n    }\n\n    /***\n     * 根据changeEvent和定义的pattern匹配key，如果所有对应PropertyChangeType为DELETED则需要清空GatewayProperties里相关集合\n     *\n     * @param changeEvent\n     * @param pattern\n     * @param existSize\n     * @return boolean\n     * @author ksewen\n     * @date 2019/5/23 2:18 PM\n     */\n    // 判断是否清除的标准，是通过指定配置项被删除的数量，是否和内存中的该配置项的数量一样。如果一样，说明被清空了\n    private boolean checkNeedClear(ConfigChangeEvent changeEvent, String pattern, int existSize) {\n        return changeEvent.changedKeys().stream().filter(key -> key.matches(pattern))\n                .filter(key -> {\n                    ConfigChange change = changeEvent.getChange(key);\n                    return PropertyChangeType.DELETED.equals(change.getChangeType());\n                }).count() == existSize;\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo03-config-apollo/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: gateway-application\n\n  cloud:\n    # Spring Cloud Gateway 配置项，全部配置在 Apollo 中\n#    gateway:\n\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\n# Apollo 相关配置项\napp:\n  id: ${spring.application.name} # 使用的 Apollo 的项目（应用）编号\napollo:\n  meta: http://127.0.0.1:8080 # Apollo Meta Server 地址\n  bootstrap:\n    enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。\n    eagerLoad:\n      enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。\n    namespaces: application # 使用的 Apollo 的命名空间，默认为 application。\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo03-config-nacos/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-08-spring-cloud-gateway</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-08-sc-gateway-demo03-config-nacos</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Gateway 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-gateway</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Config 相关依赖，将 Nacos 作为配置中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo03-config-nacos/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class GatewayApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(GatewayApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo03-config-nacos/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n\n  cloud:\n    # Spring Cloud Gateway 配置项，全部配置在 Nacos 中\n#    gateway:\n\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo03-config-nacos/src/main/resources/bootstrap.yaml",
    "content": "spring:\n  application:\n    name: gateway-application\n\n  cloud:\n    nacos:\n      # Nacos Config 配置项，对应 NacosConfigProperties 配置属性类\n      config:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        namespace: # 使用的 Nacos 的命名空间，默认为 null\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        name: # 使用的 Nacos 配置集的 dataId，默认为 spring.application.name\n        file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名，同时也是 Nacos 配置集的配置格式，默认为 properties\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo04/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-08-spring-cloud-gateway</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-08-sc-gateway-demo04</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Gateway 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-gateway</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo04/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class GatewayApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(GatewayApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo04/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: gateway-application\n\n  cloud:\n    # Spring Cloud Gateway 配置项，对应 GatewayProperties 类\n    gateway:\n      # 路由配置项，对应 RouteDefinition 数组\n      routes:\n        - id: user-service-prod\n          uri: http://www.iocoder.cn\n          predicates:\n            - Path=/**\n            - Weight=user-service, 90\n        - id: user-service-canary\n          uri: https://www.oschina.net\n          predicates:\n            - Path=/**\n            - Weight=user-service, 10\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo05-custom-gateway-filter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-08-spring-cloud-gateway</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-08-sc-gateway-demo05-custom-gateway-filter</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Gateway 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-gateway</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo05-custom-gateway-filter/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class GatewayApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(GatewayApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo05-custom-gateway-filter/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/filter/AuthGatewayFilterFactory.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo.filter;\n\nimport org.springframework.cloud.gateway.filter.GatewayFilter;\nimport org.springframework.cloud.gateway.filter.GatewayFilterChain;\nimport org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.server.reactive.ServerHttpRequest;\nimport org.springframework.http.server.reactive.ServerHttpResponse;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.StringUtils;\nimport org.springframework.web.server.ServerWebExchange;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@Component\npublic class AuthGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthGatewayFilterFactory.Config> {\n\n    public AuthGatewayFilterFactory() {\n        super(AuthGatewayFilterFactory.Config.class);\n    }\n\n    @Override\n    public GatewayFilter apply(Config config) {\n        // token 和 userId 的映射\n        Map<String, Integer> tokenMap = new HashMap<>();\n        tokenMap.put(\"yunai\", 1);\n\n        // 创建 GatewayFilter 对象\n        return new GatewayFilter() {\n\n            @Override\n            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {\n                // 获得 token\n                ServerHttpRequest request = exchange.getRequest();\n                HttpHeaders headers = request.getHeaders();\n                String token = headers.getFirst(config.getTokenHeaderName());\n\n                // 如果没有 token，则不进行认证。因为可能是无需认证的 API 接口\n                if (!StringUtils.hasText(token)) {\n                    return chain.filter(exchange);\n                }\n\n                // 进行认证\n                ServerHttpResponse response = exchange.getResponse();\n                Integer userId = tokenMap.get(token);\n\n                // 通过 token 获取不到 userId，说明认证不通过\n                if (userId == null) {\n                    // 响应 401 状态码\n                    response.setStatusCode(HttpStatus.UNAUTHORIZED);\n                    // 响应提示\n                    DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(\"认证不通过\".getBytes());\n                    return response.writeWith(Flux.just(buffer));\n                }\n\n                // 认证通过，将 userId 添加到 Header 中\n                request = request.mutate().header(config.getUserIdHeaderName(), String.valueOf(userId))\n                        .build();\n                return chain.filter(exchange.mutate().request(request).build());\n            }\n\n        };\n    }\n\n    public static class Config {\n\n        private static final String DEFAULT_TOKEN_HEADER_NAME = \"token\";\n        private static final String DEFAULT_HEADER_NAME = \"user-id\";\n\n        private String tokenHeaderName = DEFAULT_TOKEN_HEADER_NAME;\n        private String userIdHeaderName = DEFAULT_HEADER_NAME;\n\n        public String getTokenHeaderName() {\n            return tokenHeaderName;\n        }\n\n        public String getUserIdHeaderName() {\n            return userIdHeaderName;\n        }\n\n        public Config setTokenHeaderName(String tokenHeaderName) {\n            this.tokenHeaderName = tokenHeaderName;\n            return this;\n        }\n\n        public Config setUserIdHeaderName(String userIdHeaderName) {\n            this.userIdHeaderName = userIdHeaderName;\n            return this;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo05-custom-gateway-filter/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: gateway-application\n\n  cloud:\n    # Spring Cloud Gateway 配置项，对应 GatewayProperties 类\n    gateway:\n      # 路由配置项，对应 RouteDefinition 数组\n      routes:\n        - id: yudaoyuanma # 路由的编号\n          uri: http://www.iocoder.cn # 路由到的目标地址\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/blog\n          filters:\n            - StripPrefix=1\n        - id: oschina # 路由的编号\n          uri: https://www.oschina.net # 路由的目标地址\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/oschina\n          filters: # 过滤器，对请求进行拦截，实现自定义的功能，对应 FilterDefinition 数组\n            - StripPrefix=1\n      # 默认过滤器，对应 FilterDefinition 数组\n      default-filters:\n        - name: Auth\n          args:\n            token-header-name: access-token\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo06-rate-limiter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-08-spring-cloud-gateway</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-08-sc-gateway-demo06-rate-limiter</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Gateway 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-gateway</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Data Redis 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis</artifactId>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo06-rate-limiter/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class GatewayApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(GatewayApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo06-rate-limiter/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/config/GatewayConfig.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo.config;\n\nimport org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.server.ServerWebExchange;\nimport reactor.core.publisher.Mono;\n\n@Configuration\npublic class GatewayConfig {\n\n    @Bean\n    public KeyResolver ipKeyResolver() {\n        return new KeyResolver() {\n\n            @Override\n            public Mono<String> resolve(ServerWebExchange exchange) {\n                // 获取请求的 IP\n                return Mono.just(exchange.getRequest().getRemoteAddress().getHostName());\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo06-rate-limiter/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: gateway-application\n\n  cloud:\n    # Spring Cloud Gateway 配置项，对应 GatewayProperties 类\n    gateway:\n      # 路由配置项，对应 RouteDefinition 数组\n      routes:\n        - id: yudaoyuanma # 路由的编号\n          uri: http://www.iocoder.cn # 路由到的目标地址\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/blog\n          filters:\n            - StripPrefix=1\n            - name: RequestRateLimiter\n              args:\n                redis-rate-limiter.replenishRate: 1 # 令牌桶的每秒放的数量\n                redis-rate-limiter.burstCapacity: 2 # 令牌桶的最大令牌数\n                key-resolver: \"#{@ipKeyResolver}\" # 获取限流 KEY 的 Bean 的名字\n        - id: oschina # 路由的编号\n          uri: https://www.oschina.net # 路由的目标地址\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/oschina\n          filters: # 过滤器，对请求进行拦截，实现自定义的功能，对应 FilterDefinition 数组\n            - StripPrefix=1\n\n  # Redis 配置项\n  redis:\n    host: 127.0.0.1\n    port: 6379\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-hystrix/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-08-spring-cloud-gateway</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-08-sc-gateway-demo07-hystrix</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Gateway 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-gateway</artifactId>\n        </dependency>\n\n        <!-- 引入 Hystrix 相关依赖，使用它实现服务容错，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-hystrix/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class GatewayApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(GatewayApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-hystrix/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/controller/FallbackController.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.gateway.support.ServerWebExchangeUtils;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.server.ServerWebExchange;\n\n@RestController\npublic class FallbackController {\n\n    private Logger logger = LoggerFactory.getLogger(FallbackController.class);\n\n    @GetMapping(\"/fallback\")\n    public String fallback(ServerWebExchange exchange) {\n//        URI requestUrl = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);\n        Throwable executionException = exchange.getAttribute(ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR);\n        logger.error(\"[fallback][发生异常]\", executionException);\n\n        return \"服务降级...\" + executionException.getMessage();\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-hystrix/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: gateway-application\n\n  cloud:\n    # Spring Cloud Gateway 配置项，对应 GatewayProperties 类\n    gateway:\n      # 路由配置项，对应 RouteDefinition 数组\n      routes:\n        - id: hystrix_test\n          uri: http://127.0.0.1:18181\n          predicates:\n            - Path=/**\n          filters:\n            - name: Hystrix\n              args:\n                name: fallbackcmd # 对应的 Hystrix Command 名字\n                fallbackUri: forward:/fallback # 处理 Hystrix fallback 的情况，重定向到指定地址\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-sentinel/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-08-spring-cloud-gateway</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-08-sc-gateway-demo07-sentinel</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Gateway 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-gateway</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Sentinel 相关依赖，使用 Sentinel 提供服务保障，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-sentinel/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/CustomBlockRequestHandler.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo;\n\nimport com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.reactive.function.server.ServerResponse;\nimport org.springframework.web.server.ServerWebExchange;\nimport reactor.core.publisher.Mono;\n\n@Component\npublic class CustomBlockRequestHandler implements BlockRequestHandler {\n\n    private static final String DEFAULT_BLOCK_MSG_PREFIX = \"HAHAHA ~ Blocked by Sentinel: \";\n\n    @Override\n    public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable ex) {\n        return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS) // 状态码\n                .contentType(MediaType.TEXT_PLAIN) // 内容类型为 text/plain 纯文本\n                .bodyValue(DEFAULT_BLOCK_MSG_PREFIX + ex.getClass().getSimpleName()); // 错误提示\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-sentinel/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo;\n\nimport com.alibaba.cloud.sentinel.gateway.ConfigConstants;\nimport com.alibaba.csp.sentinel.config.SentinelConfig;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class GatewayApplication {\n\n    public static void main(String[] args) {\n        System.setProperty(SentinelConfig.APP_TYPE, ConfigConstants.APP_TYPE_SCG_GATEWAY); // 【重点】设置应用类型为 Spring Cloud Gateway\n        SpringApplication.run(GatewayApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-sentinel/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: gateway-application\n\n  cloud:\n    # Spring Cloud Gateway 配置项，对应 GatewayProperties 类\n    gateway:\n      # 路由配置项，对应 RouteDefinition 数组\n      routes:\n        - id: yudaoyuanma # 路由的编号\n          uri: http://www.iocoder.cn # 路由到的目标地址\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/**\n\n    sentinel:\n      eager: true # 是否饥饿加载。默认为 false 关闭\n      transport:\n        dashboard: localhost:7070 # 是否饥饿加载。默认为 false 关闭\n#      # 数据源的配置项\n#      datasource:\n#        ds1.file:\n#          file: \"classpath: sentinel-gw-flow.json\"\n#          ruleType: gw-flow\n#        ds2.file:\n#          file: \"classpath: sentinel-gw-api-group.json\"\n#          ruleType: gw-api-group\n      # Sentinel 对 Spring Cloud Gateway 的专属配置项，对应 SentinelGatewayProperties 类\n      scg:\n        order: -2147483648 # 过滤器顺序，默认为 -2147483648 最高优先级\n        fallback:\n          mode: # fallback 模式，目前有三种：response、redirect、空\n          # 专属 response 模式\n          response-status: 429 # 响应状态码，默认为 429\n          response-body: 你被 block 了... # 响应内容，默认为空\n          content-type: application/json # 内容类型，默认为 application/json\n          # 专属 redirect 模式\n          redirect: http://www.baidu.com\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-sentinel/src/main/resources/sentinel-gw-api-group.json",
    "content": "[\n  {\n    \"apiName\": \"yudaoyuanma_customized_api\",\n    \"predicateItems\": [\n      {\n        \"pattern\": \"/categories/**\",\n        \"matchStrategy\": 1\n      },\n      {\n        \"items\": [\n          {\n            \"pattern\": \"/Dubbo/good-collection/\",\n            \"matchStrategy\": 0\n          },\n          {\n            \"pattern\": \"/SkyWalking/**\",\n            \"matchStrategy\": 1\n          }\n        ]\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo07-sentinel/src/main/resources/sentinel-gw-flow.json",
    "content": "[\n  {\n    \"resource\": \"yudaoyuanma\",\n    \"count\": 3\n  },\n  {\n    \"resource\": \"yudaoyuanma_customized_api\",\n    \"count\": 1\n  }\n]\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo08-custom-global-filter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-08-spring-cloud-gateway</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-08-sc-gateway-demo08-custom-global-filter</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Gateway 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-gateway</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入定义的 Dubbo API 接口 -->\n<!--        <dependency>-->\n<!--            <groupId>cn.iocoder.springboot.labs</groupId>-->\n<!--            <artifactId>labx-07-sca-dubbo-demo02-api</artifactId>-->\n<!--            <version>1.0-SNAPSHOT</version>-->\n<!--        </dependency>-->\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo08-custom-global-filter/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class GatewayApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(GatewayApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo08-custom-global-filter/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/User03Controller.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@RestController\n@RequestMapping(\"/user03\")\npublic class User03Controller {\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n//    @GetMapping(\"/get\")\n//    public UserDTO get(@RequestParam(\"id\") Integer id) {\n//        String url = String.format(\"http://%s/user/get?id=%d\", \"demo-provider\", id);\n//        return restTemplate.getForObject(url, UserDTO.class);\n//    }\n//\n//    @PostMapping(\"/add\")\n//    public Integer add(UserAddDTO addDTO) {\n//        // 请求头\n//        HttpHeaders headers = new HttpHeaders();\n//        headers.setContentType(MediaType.APPLICATION_JSON);\n//        // 请求体\n//        String body = JSON.toJSONString(addDTO);\n//        // 创建 HttpEntity 对象\n//        HttpEntity<String> entity = new HttpEntity<>(body, headers);\n//        // 执行请求\n//        String url = String.format(\"http://%s/user/add\", \"demo-provider\");\n//        return restTemplate.postForObject(url, entity, Integer.class);\n//    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo08-custom-global-filter/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/config/RestTemplateConfig.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo.config;\n\nimport com.alibaba.cloud.dubbo.annotation.DubboTransported;\nimport org.springframework.cloud.client.loadbalancer.LoadBalanced;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.client.RestTemplate;\n\n@Configuration\npublic class RestTemplateConfig {\n\n    @Bean\n    @LoadBalanced\n    @DubboTransported(protocol = \"dubbo\")\n    public RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo08-custom-global-filter/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/filter/DubboFilter.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo.filter;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.gateway.filter.GatewayFilterChain;\nimport org.springframework.cloud.gateway.filter.GlobalFilter;\nimport org.springframework.core.Ordered;\nimport org.springframework.stereotype.Component;\nimport org.springframework.web.client.RestTemplate;\nimport org.springframework.web.server.ServerWebExchange;\nimport reactor.core.publisher.Mono;\n\nimport java.net.URI;\n\nimport static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;\nimport static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR;\n\n@Component\npublic class DubboFilter implements GlobalFilter, Ordered {\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {\n        URI url = exchange.getAttribute(GATEWAY_REQUEST_URL_ATTR);\n        String schemePrefix = exchange.getAttribute(GATEWAY_SCHEME_PREFIX_ATTR);\n        if (url == null\n                || (!\"dubbo\".equals(url.getScheme()) && !\"dubbo\".equals(schemePrefix))) {\n            return chain.filter(exchange);\n        }\n\n        String urlx = String.format(\"http://%s/user/get?id=%d\", \"demo-provider\", 1);\n//        UserDTO result = restTemplate.getForObject(url, UserDTO.class);\n        String result = restTemplate.getForObject(url, String.class);\n\n        return null;\n    }\n\n    public int getOrder() {\n        return 10100;\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo08-custom-global-filter/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: gateway-application\n\n  cloud:\n    # Spring Cloud Gateway 配置项，对应 GatewayProperties 类\n    gateway:\n      # 路由配置项，对应 RouteDefinition 数组\n      routes:\n#        - id: yudaoyuanma # 路由的编号\n#          uri: http://www.iocoder.cn # 路由到的目标地址\n#          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n#            - Path=/blog\n#          filters:\n#            - StripPrefix=1\n#        - id: oschina # 路由的编号\n#          uri: https://www.oschina.net # 路由的目标地址\n#          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n#            - Path=/oschina\n#          filters: # 过滤器，对请求进行拦截，实现自定义的功能，对应 FilterDefinition 数组\n#            - StripPrefix=1\n        - id: yudaoyuanma # 路由的编号\n          uri: dubbo://demo-provider # 路由到的目标地址\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/user/**\n#          filters:\n#            - StripPrefix=1\n\n# Dubbo 配置项，对应 DubboConfigurationProperties 类\ndubbo:\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: demo-provider # 设置订阅的应用列表，默认为 * 订阅所有应用。\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo09-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-08-spring-cloud-gateway</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-08-sc-gateway-demo09-actuator</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Gateway 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-gateway</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo09-actuator/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class GatewayApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(GatewayApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo09-actuator/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: gateway-application\n\n#  cloud:\n#    # Spring Cloud Gateway 配置项，对应 GatewayProperties 类\n#    gateway:\n#      # 路由配置项，对应 RouteDefinition 数组\n#      routes:\n#        - id: yudaoyuanma # 路由的编号\n#          uri: http://www.iocoder.cn # 路由到的目标地址\n#          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n#            - Path=/blog\n#          filters:\n#            - StripPrefix=1\n#        - id: oschina # 路由的编号\n#          uri: https://www.oschina.net # 路由的目标地址\n#          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n#            - Path=/oschina\n#          filters: # 过滤器，对请求进行拦截，实现自定义的功能，对应 FilterDefinition 数组\n#            - StripPrefix=1\n\nmanagement:\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n  endpoint:\n    # Health 端点配置项，对应 HealthProperties 配置类\n    health:\n      enabled: true # 是否开启。默认为 true 开启。\n      show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户；可选 ALWAYS 总是展示。\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo10-troubleshooting/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-08-spring-cloud-gateway</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-08-sc-gateway-demo10-troubleshooting</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Gateway 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-gateway</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo10-troubleshooting/src/main/java/cn/iocoder/springcloud/labx08/gatewaydemo/GatewayApplication.java",
    "content": "package cn.iocoder.springcloud.labx08.gatewaydemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class GatewayApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(GatewayApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-gateway-demo10-troubleshooting/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: gateway-application\n\n  cloud:\n    # Spring Cloud Gateway 配置项，对应 GatewayProperties 类\n    gateway:\n      # 路由配置项，对应 RouteDefinition 数组\n      routes:\n        - id: yudaoyuanma # 路由的编号\n          uri: http://www.iocoder.cn # 路由到的目标地址\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/blog\n          filters:\n            - StripPrefix=1\n        - id: oschina # 路由的编号\n          uri: https://www.oschina.net # 路由的目标地址\n          predicates: # 断言，作为路由的匹配条件，对应 RouteDefinition 数组\n            - Path=/oschina\n          filters: # 过滤器，对请求进行拦截，实现自定义的功能，对应 FilterDefinition 数组\n            - StripPrefix=1\n\n      # Reactor Netty 相关配置\n      httpserver:\n        wiretap: true\n      httpclient:\n        wiretap: true\n\nlogging:\n  level:\n    reactor.netty: DEBUG\n    org.springframework.cloud.gateway: TRACE\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-user-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-08-spring-cloud-gateway</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-08-sc-user-service</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-user-service/src/main/java/cn/iocoder/springcloud/labx08/userservice/UserServiceApplication.java",
    "content": "package cn.iocoder.springcloud.labx08.userservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-user-service/src/main/java/cn/iocoder/springcloud/labx08/userservice/controller/UserController.java",
    "content": "package cn.iocoder.springcloud.labx08.userservice.controller;\n\nimport cn.iocoder.springcloud.labx08.userservice.dto.UserAddDTO;\nimport cn.iocoder.springcloud.labx08.userservice.dto.UserDTO;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @GetMapping(\"/get\")\n    public UserDTO get(@RequestParam(\"id\") Integer id) {\n        return new UserDTO().setId(id)\n                .setName(\"没有昵称：\" + id)\n                .setGender(id % 2 + 1); // 1 - 男；2 - 女\n    }\n\n    @PostMapping(\"/add\")\n    public Integer add(UserAddDTO addDTO) {\n        return (int) (System.currentTimeMillis() / 1000); // 嘿嘿，随便返回一个 id\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-user-service/src/main/java/cn/iocoder/springcloud/labx08/userservice/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springcloud.labx08.userservice.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO implements Serializable {\n\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public String getName() {\n        return name;\n    }\n\n    public UserAddDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserAddDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-user-service/src/main/java/cn/iocoder/springcloud/labx08/userservice/dto/UserDTO.java",
    "content": "package cn.iocoder.springcloud.labx08.userservice.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户信息 DTO\n */\npublic class UserDTO implements Serializable {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/labx-08-sc-user-service/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: user-service # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址 \"'lb://'+serviceId\"\n\nserver:\n  port: ${random.int[10000,19999]}  # 服务器端口。默认为 8080\n#  port: 18080  # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-08-spring-cloud-gateway</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-08-sc-gateway-demo01</module>\n        <module>labx-08-sc-gateway-demo01-test</module>\n\n        <module>labx-08-sc-gateway-demo02-registry</module>\n\n        <module>labx-08-sc-gateway-demo03-config-apollo</module>\n        <module>labx-08-sc-gateway-demo03-config-nacos</module>\n\n        <module>labx-08-sc-gateway-demo04</module>\n\n        <module>labx-08-sc-gateway-demo05-custom-gateway-filter</module>\n\n        <module>labx-08-sc-gateway-demo06-rate-limiter</module>\n\n        <module>labx-08-sc-gateway-demo07-hystrix</module>\n        <module>labx-08-sc-gateway-demo07-sentinel</module>\n\n        <module>labx-08-sc-gateway-demo08-custom-global-filter</module>\n\n        <module>labx-08-sc-gateway-demo09-actuator</module>\n\n        <module>labx-08-sc-gateway-demo10-troubleshooting</module>\n\n        <module>labx-08-sc-user-service</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-08-spring-cloud-gateway/《芋道 Spring Cloud 网关 Spring Cloud Gateway 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/Spring-Cloud-Gateway/?github>\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-09-spring-cloud-apollo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-09-sc-apollo-demo</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud 基础依赖 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-commons</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-context</artifactId>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!--  引入 Apollo 客户端，内置对 Apollo 的自动化配置 -->\n        <dependency>\n            <groupId>com.ctrip.framework.apollo</groupId>\n            <artifactId>apollo-client</artifactId>\n            <version>1.5.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx09.apollodemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/config/OrderProperties.java",
    "content": "package cn.iocoder.springcloud.labx09.apollodemo.config;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n//    /**\n//     * 配置描述\n//     */\n//    private String desc;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n//    public String getDesc() {\n//        return desc;\n//    }\n//\n//    public OrderProperties setDesc(String desc) {\n//        this.desc = desc;\n//        return this;\n//    }\n\n}\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx09.apollodemo.controller;\n\nimport cn.iocoder.springcloud.labx09.apollodemo.config.OrderProperties;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private OrderProperties orderProperties;\n\n    /**\n     * 测试 @ConfigurationProperties 注解的配置属性类\n     */\n    @GetMapping(\"/test01\")\n    public OrderProperties test01() {\n        return orderProperties;\n    }\n\n    @Value(value = \"${order.pay-timeout-seconds}\")\n    private Integer payTimeoutSeconds;\n    @Value(value = \"${order.create-frequency-seconds}\")\n    private Integer createFrequencySeconds;\n\n    /**\n     * 测试 @Value 注解的属性\n     */\n    @GetMapping(\"/test02\")\n    public Map<String, Object> test02() {\n        Map<String, Object> result = new HashMap<>();\n        result.put(\"payTimeoutSeconds\", payTimeoutSeconds);\n        result.put(\"createFrequencySeconds\", createFrequencySeconds);\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo/src/main/resources/application.yaml",
    "content": "server:\n  port: 7070 # 避免和本地的 Apollo Portal 端口冲突\n\napp:\n  id: demo-application # 使用的 Apollo 的项目（应用）编号\napollo:\n  meta: http://127.0.0.1:8080 # Apollo Meta Server 地址\n  bootstrap:\n    enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。\n    eagerLoad:\n      enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。\n    namespaces: application # 使用的 Apollo 的命名空间，默认为 application。\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-auto-refresh/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-09-spring-cloud-apollo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-09-sc-apollo-demo-auto-refresh</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud 基础依赖 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-commons</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-context</artifactId>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!--  引入 Apollo 客户端，内置对 Apollo 的自动化配置 -->\n        <dependency>\n            <groupId>com.ctrip.framework.apollo</groupId>\n            <artifactId>apollo-client</artifactId>\n            <version>1.5.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-auto-refresh/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx09.apollodemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-auto-refresh/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/config/OrderProperties.java",
    "content": "package cn.iocoder.springcloud.labx09.apollodemo.config;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n//    /**\n//     * 配置描述\n//     */\n//    private String desc;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n//    public String getDesc() {\n//        return desc;\n//    }\n//\n//    public OrderProperties setDesc(String desc) {\n//        this.desc = desc;\n//        return this;\n//    }\n\n}\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-auto-refresh/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx09.apollodemo.controller;\n\nimport cn.iocoder.springcloud.labx09.apollodemo.config.OrderProperties;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private OrderProperties orderProperties;\n\n    /**\n     * 测试 @ConfigurationProperties 注解的配置属性类\n     */\n    @GetMapping(\"/test01\")\n    public OrderProperties test01() {\n        return orderProperties;\n    }\n\n    @Value(value = \"${order.pay-timeout-seconds}\")\n    private Integer payTimeoutSeconds;\n    @Value(value = \"${order.create-frequency-seconds}\")\n    private Integer createFrequencySeconds;\n\n    /**\n     * 测试 @Value 注解的属性\n     */\n    @GetMapping(\"/test02\")\n    public Map<String, Object> test02() {\n        Map<String, Object> result = new HashMap<>();\n        result.put(\"payTimeoutSeconds\", payTimeoutSeconds);\n        result.put(\"createFrequencySeconds\", createFrequencySeconds);\n        return result;\n    }\n\n    @GetMapping(\"/logger\")\n    public void logger() {\n        logger.debug(\"[logger][测试一下]\");\n    }\n\n}\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-auto-refresh/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/listener/ApolloPropertiesRefresher.java",
    "content": "package cn.iocoder.springcloud.labx09.apollodemo.listener;\n\nimport com.ctrip.framework.apollo.core.ConfigConsts;\nimport com.ctrip.framework.apollo.model.ConfigChangeEvent;\nimport com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;\nimport org.springframework.beans.BeansException;\nimport org.springframework.cloud.context.environment.EnvironmentChangeEvent;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.context.ApplicationContextAware;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ApolloPropertiesRefresher implements ApplicationContextAware {\n\n    private ApplicationContext applicationContext;\n\n    @Override\n    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {\n        this.applicationContext = applicationContext;\n    }\n\n    @ApolloConfigChangeListener(value = ConfigConsts.NAMESPACE_APPLICATION)\n    public void onChange(ConfigChangeEvent changeEvent) {\n        this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys()));\n    }\n\n}\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-auto-refresh/src/main/resources/application.yaml",
    "content": "server:\n  port: 7070 # 避免和本地的 Apollo Portal 端口冲突\n\napp:\n  id: demo-application # 使用的 Apollo 的项目（应用）编号\napollo:\n  meta: http://127.0.0.1:8080 # Apollo Meta Server 地址\n  bootstrap:\n    enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。\n    eagerLoad:\n      enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。\n    namespaces: application # 使用的 Apollo 的命名空间，默认为 application。\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-jasypt/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-09-spring-cloud-apollo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-09-sc-apollo-demo-jasypt</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud 基础依赖 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-commons</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-context</artifactId>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!--  引入 Apollo 客户端，内置对 Apollo 的自动化配置 -->\n        <dependency>\n            <groupId>com.ctrip.framework.apollo</groupId>\n            <artifactId>apollo-client</artifactId>\n            <version>1.5.1</version>\n        </dependency>\n\n        <!-- 实现对 Jasypt 实现自动化配置 -->\n        <dependency>\n            <groupId>com.github.ulisesbocchio</groupId>\n            <artifactId>jasypt-spring-boot-starter</artifactId>\n            <version>3.0.2</version>\n            <!--            <version>2.1.2</version>-->\n            <!--            <scope>test</scope>-->\n        </dependency>\n\n        <!-- 方便等会写单元测试 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-jasypt/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx09.apollodemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-jasypt/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx09.apollodemo.controller;\n\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Value(\"${xxx-password:}\")\n    private String xxxPassword;\n\n    @GetMapping(\"/test\")\n    public String test() {\n        return xxxPassword;\n    }\n\n}\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-jasypt/src/main/resources/application.yaml",
    "content": "server:\n  port: 7070 # 避免和本地的 Apollo Portal 端口冲突\n\napp:\n  id: demo-application-jasypt # 使用的 Apollo 的项目（应用）编号\napollo:\n  meta: http://127.0.0.1:8080 # Apollo Meta Server 地址\n  bootstrap:\n    enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。\n    eagerLoad:\n      enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。\n    namespaces: application # 使用的 Apollo 的命名空间，默认为 application。\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-jasypt/src/test/java/cn/iocoder/springcloud/labx09/apollodemo/JasyptTest.java",
    "content": "package cn.iocoder.springcloud.labx09.apollodemo;\n\nimport org.jasypt.encryption.StringEncryptor;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest\npublic class JasyptTest {\n\n    @Autowired\n    private StringEncryptor encryptor;\n\n    @Test\n    public void encode() {\n        // 第一个加密\n        String password = \"woshimima\";\n        System.out.println(encryptor.encrypt(password));\n\n        // 第二个加密\n        password = \"bushimima\";\n        System.out.println(encryptor.encrypt(password));\n    }\n\n    @Value(\"${xxx-password:}\")\n    private String xxxPassword;\n\n    @Test\n    public void print() {\n        System.out.println(xxxPassword);\n    }\n\n//    @Value(\"${jasypt.encryptor.password}\")\n//    private String password;\n\n}\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-multi/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-09-spring-cloud-apollo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-09-sc-apollo-demo-multi</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud 基础依赖 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-commons</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-context</artifactId>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!--  引入 Apollo 客户端，内置对 Apollo 的自动化配置 -->\n        <dependency>\n            <groupId>com.ctrip.framework.apollo</groupId>\n            <artifactId>apollo-client</artifactId>\n            <version>1.5.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-multi/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx09.apollodemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.core.env.Environment;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class);\n\n        // 查看 Environment\n        Environment environment = context.getEnvironment();\n        System.out.println(environment);\n    }\n\n}\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-multi/src/main/resources/application.yaml",
    "content": "server:\n  port: 7070 # 避免和本地的 Apollo Portal 端口冲突\n\napp:\n  id: demo-application-multi # 使用的 Apollo 的项目（应用）编号\napollo:\n  meta: http://127.0.0.1:8080 # Apollo Meta Server 地址\n  bootstrap:\n    enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。\n    eagerLoad:\n      enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。\n    namespaces: application, db # 使用的 Apollo 的命名空间，默认为 application。\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-profiles/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-09-spring-cloud-apollo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-09-sc-apollo-demo-profiles</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud 基础依赖 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-commons</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-context</artifactId>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!--  引入 Apollo 客户端，内置对 Apollo 的自动化配置 -->\n        <dependency>\n            <groupId>com.ctrip.framework.apollo</groupId>\n            <artifactId>apollo-client</artifactId>\n            <version>1.5.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-profiles/src/main/java/cn/iocoder/springcloud/labx09/apollodemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx09.apollodemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-profiles/src/main/resources/application-dev.yaml",
    "content": "app:\n  id: demo-application-profiles # 使用的 Apollo 的项目（应用）编号\napollo:\n  meta: http://127.0.0.1:8080 # Apollo Meta Server 地址\n  bootstrap:\n    enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。\n    eagerLoad:\n      enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。\n    namespaces: application # 使用的 Apollo 的命名空间，默认为 application。\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-profiles/src/main/resources/application-prod.yaml",
    "content": "app:\n  id: demo-application-profiles # 使用的 Apollo 的项目（应用）编号\napollo:\n  meta: http://127.0.0.1:18080 # Apollo Meta Server 地址\n  bootstrap:\n    enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。\n    eagerLoad:\n      enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。\n    namespaces: application # 使用的 Apollo 的命名空间，默认为 application。\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/labx-09-sc-apollo-demo-profiles/src/main/resources/application.yaml",
    "content": "#server:\n#  port: 7070\n\nspring:\n  application:\n    name: demo-application\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-09-spring-cloud-apollo</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-09-sc-apollo-demo</module>\n        <module>labx-09-sc-apollo-demo-profiles</module>\n        <module>labx-09-sc-apollo-demo-auto-refresh</module>\n        <module>labx-09-sc-apollo-demo-jasypt</module>\n        <module>labx-09-sc-apollo-demo-multi</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-09-spring-cloud-apollo/《芋道 Spring Cloud 配置中心 Apollo 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/Apollo/?github>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-ack/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-sc-stream-rabbitmq-consumer-ack</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-ack/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-ack/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message;\nimport com.rabbitmq.client.Channel;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.amqp.support.AmqpHeaders;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Header;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\nimport java.io.IOException;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    private AtomicInteger index = new AtomicInteger();\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message,\n                          @Header(AmqpHeaders.CHANNEL) Channel channel,\n                          @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws IOException {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n        // 提交消费进度\n//        if (message.getId() % 2 == 1) {\n        if (index.incrementAndGet() == 1) {\n            // ack 确认消息\n            // 第二个参数 multiple ，用于批量确认消息，为了减少网络流量，手动确认可以被批处。\n            // 1. 当 multiple 为 true 时，则可以一次性确认 deliveryTag 小于等于传入值的所有消息\n            // 2. 当 multiple 为 false 时，则只确认当前 deliveryTag 对应的消息\n            channel.basicAck(deliveryTag, false);\n        }\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-ack/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-ack/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-ack/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n          binder: rabbit001  # 设置使用的 Binder 名字\n      # RabbitMQ 自定义 Binding 配置项，对应 RabbitBindingProperties Map\n      rabbit:\n        bindings:\n          demo01-input:\n            # RabbitMQ Consumer 配置项，对应 RabbitConsumerProperties 类\n            consumer:\n              acknowledge-mode: MANUAL # 消费消息的确认模式，默认为 AUTO 自动确认\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-sc-stream-rabbitmq-consumer-actuator</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-actuator/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-actuator/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-actuator/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-actuator/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-actuator/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n          binder: rabbit001  # 设置使用的 Binder 名字\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n\nmanagement:\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n  endpoint:\n    # Health 端点配置项，对应 HealthProperties 配置类\n    health:\n      enabled: true # 是否开启。默认为 true 开启。\n      show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户；可选 ALWAYS 总是展示。\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-batch/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-sc-stream-rabbitmq-consumer-batch</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-batch/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-batch/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload List<Demo01Message> message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-batch/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-batch/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-batch/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n          binder: rabbit001  # 设置使用的 Binder 名字\n          # Consumer 配置项，对应 ConsumerProperties 类\n          consumer:\n            batch-mode: true # 是否批量消费默认，默认为 false\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-broadcasting/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-sc-stream-rabbitmq-consumer-broadcasting</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-broadcasting/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-broadcasting/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-broadcasting/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-broadcasting/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-broadcasting/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n#          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n          binder: rabbit001  # 设置使用的 Binder 名字\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-concurrency/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-sc-stream-rabbitmq-consumer-concurrency</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-concurrency/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-concurrency/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n//\n//        try {\n//            Thread.sleep(10 * 1000L);\n//        } catch (InterruptedException ignored) {\n//        }\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-concurrency/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-concurrency/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-concurrency/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n          binder: rabbit001  # 设置使用的 Binder 名字\n          # Consumer 配置项，对应 ConsumerProperties 类\n          consumer:\n            concurrency: 2 # 每个 Consumer 消费线程数的初始大小，默认为 1\n      # RabbitMQ 自定义 Binding 配置项，对应 RabbitBindingProperties Map\n      rabbit:\n        bindings:\n          demo01-input:\n            # RabbitMQ Consumer 配置项，对应 RabbitConsumerProperties 类\n            consumer:\n              max-concurrency: 10 # 每个 Consumer 消费线程数的最大大小，默认为 1\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-delay/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-sc-stream-rabbitmq-consumer-delay</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-delay/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-delay/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-delay/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-delay/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-delay/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-02 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n          binder: rabbit001  # 设置使用的 Binder 名字\n      # RabbitMQ 自定义 Binding 配置项，对应 RabbitBindingProperties Map\n      rabbit:\n        bindings:\n          demo01-input:\n            # RabbitMQ Consumer 配置项，对应 RabbitConsumerProperties 类\n            consumer:\n              delayed-exchange: true # 是否使用 x-delayed-message 类型的 Exchange，即延迟消息，默认为 false\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-sc-stream-rabbitmq-consumer-demo</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-demo/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-demo/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-demo/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-demo/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-demo/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n          binder: rabbit001  # 设置使用的 Binder 名字\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-error-handler/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-sc-stream-rabbitmq-consumer-error-handler</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-error-handler/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-error-handler/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.integration.annotation.ServiceActivator;\nimport org.springframework.integration.context.IntegrationContextUtils;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.messaging.support.ErrorMessage;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT) // 对应 DEMO-TOPIC-01.demo01-consumer-group-DEMO-TOPIC-01\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n        // <X> 注意，此处抛出一个 RuntimeException 异常，模拟消费失败\n        throw new RuntimeException(\"我就是故意抛出一个异常\");\n    }\n\n    @ServiceActivator(inputChannel = \"DEMO-TOPIC-01.demo01-consumer-group-DEMO-TOPIC-01.errors\")\n    public void handleError(ErrorMessage errorMessage) {\n        logger.error(\"[handleError][payload：{}]\", errorMessage.getPayload().getMessage());\n        logger.error(\"[handleError][originalMessage：{}]\", errorMessage.getOriginalMessage());\n        logger.error(\"[handleError][headers：{}]\", errorMessage.getHeaders());\n    }\n\n    @StreamListener(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME) // errorChannel\n    public void globalHandleError(ErrorMessage errorMessage) {\n        logger.error(\"[globalHandleError][payload：{}]\", errorMessage.getPayload().getMessage());\n        logger.error(\"[globalHandleError][originalMessage：{}]\", errorMessage.getOriginalMessage());\n        logger.error(\"[globalHandleError][headers：{}]\", errorMessage.getHeaders());\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-error-handler/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-error-handler/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-error-handler/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n          binder: rabbit001  # 设置使用的 Binder 名字\n          # Consumer 配置项，对应 ConsumerProperties 类\n          consumer:\n            max-attempts: 3 # 重试次数，默认为 3 次。\n            back-off-initial-interval: 3000 # 重试间隔的初始值，单位毫秒，默认为 1000\n            back-off-multiplier: 2.0 # 重试间隔的递乘系数，默认为 2.0\n            back-off-max-interval: 10000 # 重试间隔的最大值，单位毫秒，默认为 10000\n      # RabbitMQ 自定义 Binding 配置项，对应 RabbitBindingProperties Map\n      rabbit:\n        bindings:\n          demo01-input:\n            # RabbitMQ Consumer 配置项，对应 RabbitConsumerProperties 类\n            consumer:\n              auto-bind-dlq: true # 是否创建对应的死信队列，并进行绑定，默认为 false。\n              republish-to-dlq: true # 消费失败的消息发布到对应的死信队列时，是否添加异常异常的信息到消息头\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-filter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-sc-stream-rabbitmq-consumer-filter</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-filter/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-filter/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(value = MySink.DEMO01_INPUT, condition = \"headers['tag'] == 'yunai'\")\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-filter/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-filter/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-filter/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n          binder: rabbit001  # 设置使用的 Binder 名字\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-partitioning/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-sc-stream-rabbitmq-consumer-partitioning</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-partitioning/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-partitioning/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-partitioning/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-partitioning/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-partitioning/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-03 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n          binder: rabbit001  # 设置使用的 Binder 名字\n          consumer:\n            partitioned: true\n            instance-index: 1\n\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-retry/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-sc-stream-rabbitmq-consumer-retry</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-retry/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-retry/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n        // <X> 注意，此处抛出一个 RuntimeException 异常，模拟消费失败\n        throw new RuntimeException(\"我就是故意抛出一个异常\");\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-retry/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-retry/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-consumer-retry/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n          binder: rabbit001  # 设置使用的 Binder 名字\n          # Consumer 配置项，对应 ConsumerProperties 类\n          consumer:\n            max-attempts: 3 # 重试次数，默认为 3 次。\n            back-off-initial-interval: 3000 # 重试间隔的初始值，单位毫秒，默认为 1000\n            back-off-multiplier: 2.0 # 重试间隔的递乘系数，默认为 2.0\n            back-off-max-interval: 10000 # 重试间隔的最大值，单位毫秒，默认为 10000\n      # RabbitMQ 自定义 Binding 配置项，对应 RabbitBindingProperties Map\n      rabbit:\n        bindings:\n          demo01-input:\n            # RabbitMQ Consumer 配置项，对应 RabbitConsumerProperties 类\n            consumer:\n              auto-bind-dlq: true # 是否创建对应的死信队列，并进行绑定，默认为 false。\n              republish-to-dlq: true # 消费失败的消息发布到对应的死信队列时，是否添加异常异常的信息到消息头\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-sc-stream-rabbitmq-producer-actuator</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-actuator/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-actuator/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.controller;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.Demo01Message;\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send\")\n    public boolean send() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .build();\n        // 发送消息\n        return mySource.demo01Output().send(springMessage);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-actuator/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-actuator/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/message/MySource.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-actuator/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          binder: rabbit001 # 设置使用的 Binder 名字\n\nserver:\n  port: 18080\n\nmanagement:\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n  endpoint:\n    # Health 端点配置项，对应 HealthProperties 配置类\n    health:\n      enabled: true # 是否开启。默认为 true 开启。\n      show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户；可选 ALWAYS 总是展示。\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-batch/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-sc-stream-rabbitmq-producer-batch</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-batch/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-batch/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.controller;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.Demo01Message;\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send_batch\")\n    public boolean sendBatch() throws InterruptedException {\n        // 发送 3 条消息，每条中间间隔 10 秒\n        for (int i = 0; i < 3; i++) {\n            // 创建 Message\n            Demo01Message message = new Demo01Message()\n                    .setId(new Random().nextInt());\n            // 创建 Spring Message 对象\n            Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                    .build();\n            // 发送消息\n            mySource.demo01Output().send(springMessage);\n\n            // 故意每条消息之间，隔离 10 秒\n            logger.info(\"[sendBatch][发送编号：[{}] 发送成功]\", message.getId());\n            Thread.sleep(10 * 1000L);\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-batch/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-batch/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/message/MySource.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-batch/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          binder: rabbit001 # 设置使用的 Binder 名字\n      # RabbitMQ 自定义 Binding 配置项，对应 RabbitBindingProperties Map\n      rabbit:\n        bindings:\n          demo01-output:\n            # RabbitMQ Producer 配置项，对应 RabbitProducerProperties 类\n            producer:\n              batching-enabled: true # 是否开启批量发送功能，默认为 false\n              batch-size: 100 # 超过收集的消息数量的最大条数，默认为 100\n              batch-buffer-limit: 10000 # 每次批量发送消息的最大内存，默认为 10000\n              batch-timeout: 30000 # 超过收集的时间的最大等待时长，单位：毫秒，默认为 5000\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-confirm/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-sc-stream-rabbitmq-producer-confirm</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-confirm/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-confirm/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.controller;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.Demo01Message;\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send\")\n    public boolean send() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .build();\n        // 发送消息\n        return mySource.demo01Output().send(springMessage);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-confirm/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-confirm/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/message/Demo01ProducerConfirmCallback.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.integration.annotation.ServiceActivator;\nimport org.springframework.messaging.Message;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01ProducerConfirmCallback {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @ServiceActivator(inputChannel = \"demo01-producer-confirm\")\n    public void onPublisherConfirm(Message message) {\n        logger.info(\"[onPublisherConfirm][headers：{}]\", message.getHeaders());\n        logger.info(\"[onPublisherConfirm][payload：{}]\", message.getPayload());\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-confirm/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/message/Demo01ProducerReturnCallback.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.integration.annotation.ServiceActivator;\nimport org.springframework.integration.context.IntegrationContextUtils;\nimport org.springframework.messaging.support.ErrorMessage;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01ProducerReturnCallback {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @ServiceActivator(inputChannel = \"DEMO-TOPIC-01.errors\")\n    public void handleError(ErrorMessage errorMessage) {\n        logger.error(\"[handleError][headers：{}]\", errorMessage.getHeaders());\n        logger.error(\"[handleError][payload：{}]\", errorMessage.getPayload().getMessage());\n        logger.error(\"[handleError][originalMessage：{}]\", errorMessage.getOriginalMessage());\n    }\n\n    @StreamListener(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME) // errorChannel\n    public void globalHandleError(ErrorMessage errorMessage) {\n        logger.error(\"[globalHandleError][payload：{}]\", errorMessage.getPayload().getMessage());\n        logger.error(\"[globalHandleError][originalMessage：{}]\", errorMessage.getOriginalMessage());\n        logger.error(\"[globalHandleError][headers：{}]\", errorMessage.getHeaders());\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-confirm/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/message/MySource.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-confirm/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n                publisher-returns: true # 设置消息是否回退，默认为 false\n                publisher-confirm-type: simple # 设置开启消息确认模型，默认为 null 不进行确认\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          binder: rabbit001 # 设置使用的 Binder 名字\n          producer:\n            error-channel-enabled: true # 是否开启异常 Channel，默认为 false 关闭\n      # RabbitMQ 自定义 Binding 配置项，对应 RabbitBindingProperties Map\n      rabbit:\n        bindings:\n          demo01-output:\n            # RabbitMQ Producer 配置项，对应 RabbitProducerProperties 类\n            producer:\n              confirm-ack-channel: demo01-producer-confirm # 设置发送确认的 Channel，默认为 null\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-delay/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-sc-stream-rabbitmq-producer-delay</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-delay/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-delay/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.controller;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.Demo01Message;\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send_delay\")\n    public boolean sendDelay() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .setHeader(\"x-delay\", 5000) // 设置延迟时间，单位：毫秒\n                .build();\n        // 发送消息\n        boolean sendResult = mySource.demo01Output().send(springMessage);\n        logger.info(\"[sendDelay][发送消息完成, 结果 = {}]\", sendResult);\n        return sendResult;\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-delay/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-delay/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/message/MySource.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-delay/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-02 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          binder: rabbit001 # 设置使用的 Binder 名字\n      # RabbitMQ 自定义 Binding 配置项，对应 RabbitBindingProperties Map\n      rabbit:\n        bindings:\n          demo01-output:\n            # RabbitMQ Producer 配置项，对应 RabbitProducerProperties 类\n            producer:\n              delayed-exchange: true # 是否使用 x-delayed-message 类型的 Exchange，即延迟消息，默认为 false\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-sc-stream-rabbitmq-producer-demo</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-demo/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-demo/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.controller;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.Demo01Message;\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send\")\n    public boolean send() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .build();\n        // 发送消息\n        return mySource.demo01Output().send(springMessage);\n    }\n\n    @GetMapping(\"/send_tag\")\n    public boolean sendTag() {\n        for (String tag : new String[]{\"yunai\", \"yutou\", \"tudou\"}) {\n            // 创建 Message\n            Demo01Message message = new Demo01Message()\n                    .setId(new Random().nextInt());\n            // 创建 Spring Message 对象\n            Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                    .setHeader(\"tag\", tag) // 设置 Tag\n                    .build();\n            // 发送消息\n            mySource.demo01Output().send(springMessage);\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-demo/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-demo/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/message/MySource.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-demo/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          binder: rabbit001 # 设置使用的 Binder 名字\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-partitioning/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-sc-stream-rabbitmq-producer-partitioning</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-partitioning/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-partitioning/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.controller;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.Demo01Message;\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send_orderly\")\n    public boolean send() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .build();\n        // 发送消息\n        return mySource.demo01Output().send(springMessage);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-partitioning/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-partitioning/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/message/MySource.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-partitioning/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-03 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          binder: rabbit001 # 设置使用的 Binder 名字\n          # Producer 配置项，对应 ProducerProperties 类\n          producer:\n            partition-key-expression: payload['id'] # 分区 key 表达式。该表达式基于 Spring EL，从消息中获得分区 key。\n            partition-count: 2 # 分区大小，默认为 1 分区\n#            required-groups: demo01-consumer-group-DEMO-TOPIC-01 #only applicable for rabbit\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-transaction/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-sc-stream-rabbitmq-producer-transaction</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-transaction/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-transaction/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/config/TransactionConfig.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.config;\n\nimport org.springframework.amqp.rabbit.connection.ConnectionFactory;\nimport org.springframework.amqp.rabbit.transaction.RabbitTransactionManager;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.transaction.annotation.EnableTransactionManagement;\n\n@Configuration\n@EnableTransactionManagement\npublic class TransactionConfig {\n\n    @Bean\n    public RabbitTransactionManager rabbitTransactionManager(ConnectionFactory connectionFactory) {\n        return new RabbitTransactionManager(connectionFactory);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-transaction/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.controller;\n\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.Demo01Message;\nimport cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message.MySource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @Transactional\n    @GetMapping(\"/send_transaction\")\n    public void sendTransaction() throws InterruptedException {\n        // 创建 Message\n        int id = new Random().nextInt();\n        Demo01Message message = new Demo01Message()\n                .setId(id);\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .build();\n        // 发送消息\n        mySource.demo01Output().send(springMessage);\n        logger.info(\"[syncSend][发送编号：[{}] 发送成功]\", id);\n\n        // <X> 等待\n        Thread.sleep(10 * 1000L);\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-transaction/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-transaction/src/main/java/cn/iocoder/springcloud/labx10/rabbitmqdemo/producerdemo/message/MySource.java",
    "content": "package cn.iocoder.springcloud.labx10.rabbitmqdemo.producerdemo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/labx-10-sc-stream-rabbitmq-producer-transaction/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          binder: rabbit001 # 设置使用的 Binder 名字\n      # RabbitMQ 自定义 Binding 配置项，对应 RabbitBindingProperties Map\n      rabbit:\n        bindings:\n          demo01-output:\n            # RabbitMQ Producer 配置项，对应 RabbitProducerProperties 类\n            producer:\n              transacted: true # 是否开启事务功能，默认为 false\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-10-spring-cloud-stream-rabbitmq</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-10-sc-stream-rabbitmq-producer-demo</module>\n        <module>labx-10-sc-stream-rabbitmq-consumer-demo</module>\n\n        <module>labx-10-sc-stream-rabbitmq-producer-delay</module>\n        <module>labx-10-sc-stream-rabbitmq-consumer-delay</module>\n\n        <!--搭配 <module>labx-10-sc-stream-rabbitmq-producer-demo</module> 作为生产者-->\n        <module>labx-10-sc-stream-rabbitmq-consumer-retry</module>\n\n        <!--搭配 <module>labx-10-sc-stream-rabbitmq-producer-demo</module> 作为生产者-->\n        <module>labx-10-sc-stream-rabbitmq-consumer-error-handler</module>\n\n        <!--搭配 <module>labx-10-sc-stream-rabbitmq-producer-demo</module> 作为生产者-->\n        <module>labx-10-sc-stream-rabbitmq-consumer-broadcasting</module>\n\n        <module>labx-10-sc-stream-rabbitmq-producer-partitioning</module>\n        <module>labx-10-sc-stream-rabbitmq-consumer-partitioning</module>\n\n        <!--搭配 <module>labx-10-sc-stream-rabbitmq-producer-demo</module> 作为生产者-->\n        <module>labx-10-sc-stream-rabbitmq-consumer-concurrency</module>\n\n        <!--搭配 <module>labx-10-sc-stream-rabbitmq-producer-demo</module> 作为生产者-->\n        <module>labx-10-sc-stream-rabbitmq-consumer-filter</module>\n\n        <module>labx-10-sc-stream-rabbitmq-producer-transaction</module>\n        <!--搭配 <module>labx-10-sc-stream-rabbitmq-consumer-demo</module> 作为消费者-->\n\n        <!--搭配 <module>labx-10-sc-stream-rabbitmq-producer-demo</module> 作为生产者-->\n        <module>labx-10-sc-stream-rabbitmq-consumer-ack</module>\n\n        <module>labx-10-sc-stream-rabbitmq-producer-confirm</module>\n        <!--搭配 <module>labx-10-sc-stream-rabbitmq-consumer-demo</module> 作为消费者-->\n\n        <module>labx-10-sc-stream-rabbitmq-producer-batch</module>\n        <!--搭配 <module>labx-10-sc-stream-rabbitmq-consumer-demo</module> 作为消费者-->\n\n        <!--搭配 <module>labx-10-sc-stream-rabbitmq-producer-batch</module> 作为生产者-->\n        <module>labx-10-sc-stream-rabbitmq-consumer-batch</module>\n\n        <module>labx-10-sc-stream-rabbitmq-producer-actuator</module>\n        <module>labx-10-sc-stream-rabbitmq-consumer-actuator</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-10-spring-cloud-stream-rabbitmq/《芋道 Spring Cloud 消息队列 RabbitMQ 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/RabbitMQ/?github>\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-ack/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-11-spring-cloud-stream-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-11-sc-stream-kafka-consumer-ack</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-ack/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-ack/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.kafka.support.Acknowledgment;\nimport org.springframework.kafka.support.KafkaHeaders;\nimport org.springframework.messaging.handler.annotation.Header;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\nimport java.util.concurrent.atomic.AtomicInteger;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    private AtomicInteger index = new AtomicInteger();\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message,\n                          @Header(KafkaHeaders.ACKNOWLEDGMENT) Acknowledgment acknowledgment) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n        // 提交消费进度\n//        if (message.getId() % 2 == 1) {\n        if (index.incrementAndGet() == 1) {\n            acknowledgment.acknowledge();\n        }\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-ack/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-ack/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-ack/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n#      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group # 消费者分组\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n        # Kafka Binding 配置项，对应 KafkaBindingProperties 类\n        bindings:\n          demo01-input:\n            # Kafka Consumer 配置项，对应 KafkaConsumerProperties 类\n            consumer:\n              auto-commit-offset: false # 是否自动提交消费进度，默认为 true 自动提交。\n              ack-each-record: true # 是否每一条消息都进行提交消费进度，默认为 false 在每一批消费完成后一起提交。\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-11-spring-cloud-stream-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-11-sc-stream-kafka-consumer-actuator</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-actuator/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-actuator/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-actuator/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-actuator/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-actuator/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n#      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group # 消费者分组\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n\nmanagement:\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n  endpoint:\n    # Health 端点配置项，对应 HealthProperties 配置类\n    health:\n      enabled: true # 是否开启。默认为 true 开启。\n      show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户；可选 ALWAYS 总是展示。\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-batch/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-11-spring-cloud-stream-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-11-sc-stream-kafka-consumer-batch</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-batch/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-batch/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\nimport java.util.List;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload List<Demo01Message> messages) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), messages);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-batch/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-batch/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-batch/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n#      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group # 消费者分组\n          # Consumer 配置项，对应 ConsumerProperties 类\n          consumer:\n            batch-mode: true # 是否批量消费默认，默认为 false\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n          configuration:\n            fetch.max.wait.ms: 10000 # poll 一次拉取的阻塞的最大时长，单位：毫秒。这里指的是阻塞拉取需要满足至少 fetch-min-size 大小的消息\n            fetch.min.bytes: 1024 # poll 一次消息拉取的最小数据量，单位：字节\n            max.poll.records: 100 # poll 一次消息拉取的最大数量\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-broadcasting/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-11-spring-cloud-stream-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-11-sc-stream-kafka-consumer-broadcasting</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-broadcasting/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-broadcasting/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-broadcasting/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-broadcasting/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-broadcasting/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n#      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n#          group: demo01-consumer-group # 消费者分组\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-concurrency/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-11-spring-cloud-stream-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-11-sc-stream-kafka-consumer-concurrency</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-concurrency/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-concurrency/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-concurrency/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-concurrency/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-concurrency/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n#      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group # 消费者分组\n          # Consumer 配置项，对应 ConsumerProperties 类\n          consumer:\n            concurrency: 2 # 每个 Consumer 消费线程数的初始大小，默认为 1\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-11-spring-cloud-stream-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-11-sc-stream-kafka-consumer-demo</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-demo/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-demo/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-demo/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-demo/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-demo/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n#      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group # 消费者分组\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-error-handler/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-11-spring-cloud-stream-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-11-sc-stream-kafka-consumer-error-handler</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-error-handler/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-error-handler/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.integration.annotation.ServiceActivator;\nimport org.springframework.integration.context.IntegrationContextUtils;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.messaging.support.ErrorMessage;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT) // 对应 DEMO-TOPIC-01.demo01-consumer-group\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n        // <X> 注意，此处抛出一个 RuntimeException 异常，模拟消费失败\n        throw new RuntimeException(\"我就是故意抛出一个异常\");\n    }\n\n    @ServiceActivator(inputChannel = \"DEMO-TOPIC-01.demo01-consumer-group.errors\")\n    public void handleError(ErrorMessage errorMessage) {\n        logger.error(\"[handleError][payload：{}]\", errorMessage.getPayload().getMessage());\n        logger.error(\"[handleError][originalMessage：{}]\", errorMessage.getOriginalMessage());\n        logger.error(\"[handleError][headers：{}]\", errorMessage.getHeaders());\n    }\n\n    @StreamListener(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME) // errorChannel\n    public void globalHandleError(ErrorMessage errorMessage) {\n        logger.error(\"[globalHandleError][payload：{}]\", errorMessage.getPayload().getMessage());\n        logger.error(\"[globalHandleError][originalMessage：{}]\", errorMessage.getOriginalMessage());\n        logger.error(\"[globalHandleError][headers：{}]\", errorMessage.getHeaders());\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-error-handler/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-error-handler/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-error-handler/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n#      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group # 消费者分组\n          # Consumer 配置项，对应 ConsumerProperties 类\n          consumer:\n            max-attempts: 3 # 重试次数，默认为 3 次。\n            back-off-initial-interval: 3000 # 重试间隔的初始值，单位毫秒，默认为 1000\n            back-off-multiplier: 2.0 # 重试间隔的递乘系数，默认为 2.0\n            back-off-max-interval: 10000 # 重试间隔的最大值，单位毫秒，默认为 10000\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n        # Kafka Binding 配置项，对应 KafkaBindingProperties 类\n        bindings:\n          demo01-input:\n            # Kafka Consumer 配置项，对应 KafkaConsumerProperties 类\n            consumer:\n              enable-dlq: true # 是否开启死信队列，默认为 false 关闭\n              dlq-name: # 死信队列名，默认为 `errors.{topicName}.{consumerGroup}`\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-filter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-11-spring-cloud-stream-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-11-sc-stream-kafka-consumer-filter</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-filter/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-filter/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(value = MySink.DEMO01_INPUT, condition = \"headers['tag'] == 'yunai'\")\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-filter/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-filter/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-filter/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n#      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group # 消费者分组\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-partitioning/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-11-spring-cloud-stream-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-11-sc-stream-kafka-consumer-partitioning</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-partitioning/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-partitioning/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.Message;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(Message<?> message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-partitioning/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-partitioning/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-partitioning/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n#      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group # 消费者分组\n          # Consumer 配置项，对应 ConsumerProperties 类\n          consumer:\n            concurrency: 2 # 每个 Consumer 消费线程数的初始大小，默认为 1\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-retry/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-11-spring-cloud-stream-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-11-sc-stream-kafka-consumer-retry</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-retry/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-retry/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n        // <X> 注意，此处抛出一个 RuntimeException 异常，模拟消费失败\n        throw new RuntimeException(\"我就是故意抛出一个异常\");\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-retry/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-retry/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-retry/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n#      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group # 消费者分组\n          # Consumer 配置项，对应 ConsumerProperties 类\n          consumer:\n            max-attempts: 3 # 重试次数，默认为 3 次。\n            back-off-initial-interval: 3000 # 重试间隔的初始值，单位毫秒，默认为 1000\n            back-off-multiplier: 2.0 # 重试间隔的递乘系数，默认为 2.0\n            back-off-max-interval: 10000 # 重试间隔的最大值，单位毫秒，默认为 10000\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n        # Kafka Binding 配置项，对应 KafkaBindingProperties 类\n        bindings:\n          demo01-input:\n            # Kafka Consumer 配置项，对应 KafkaConsumerProperties 类\n            consumer:\n              enable-dlq: true # 是否开启死信队列，默认为 false 关闭\n              dlq-name: # 死信队列名，默认为 `errors.{topicName}.{consumerGroup}`\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-transaction/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-11-spring-cloud-stream-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-11-sc-stream-kafka-consumer-transaction</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-transaction/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n//    @Bean\n//    public ListenerContainerCustomizer<AbstractMessageListenerContainer<byte[], byte[]>> customizer() {\n//        return (container, dest, group) -> {\n//            KafkaTransactionManager<?, ?> tm = (KafkaTransactionManager<?, ?>) container.getContainerProperties()\n//                    .getTransactionManager();\n//            tm.setTransactionSynchronization(AbstractPlatformTransactionManager.SYNCHRONIZATION_ON_ACTUAL_TRANSACTION);\n//        };\n//    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-transaction/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-transaction/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-transaction/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-consumer-transaction/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n#      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group # 消费者分组\n\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n        # Kafka Binding 配置项，对应 KafkaBindingProperties 类\n        bindings:\n          demo01-input:\n            # Kafka Consumer 配置项，对应 KafkaConsumerProperties 类\n            consumer:\n              configuration:\n                isolation:\n                  level: read_committed # 读取已提交的消息\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-11-spring-cloud-stream-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-11-sc-stream-kafka-producer-actuator</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-actuator/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-actuator/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.controller;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.Demo01Message;\nimport cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send\")\n    public boolean send() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .build();\n        // 发送消息\n        boolean result = mySource.demo01Output().send(springMessage);\n        logger.info(\"[send][发送编号：[{}] 发送成功]\", message.getId());\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-actuator/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-actuator/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/message/MySource.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-actuator/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n#      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n        # Kafka 自定义 Binding 配置项，对应 KafkaBindingProperties Map\n        bindings:\n          demo01-output:\n            # Kafka Producer 配置项，对应 KafkaProducerProperties 类\n            producer:\n              sync: true # 是否同步发送消息，默认为 false 异步。\n\nserver:\n  port: 18080\n\nmanagement:\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n  endpoint:\n    # Health 端点配置项，对应 HealthProperties 配置类\n    health:\n      enabled: true # 是否开启。默认为 true 开启。\n      show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户；可选 ALWAYS 总是展示。\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-batch/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-11-spring-cloud-stream-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-11-sc-stream-kafka-producer-batch</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-batch/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-batch/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.controller;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.Demo01Message;\nimport cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send_batch\")\n    public boolean sendBatch() {\n        for (int i = 0; i < 3; i++) {\n            // 创建 Message\n            int id = new Random().nextInt();\n            Demo01Message message = new Demo01Message()\n                    .setId(id);\n            // 创建 Spring Message 对象\n            Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                    .build();\n            // 发送消息\n            mySource.demo01Output().send(springMessage);\n            logger.info(\"[send_batch][发送编号：[{}] 发送成功]\", id);\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-batch/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-batch/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/message/MySource.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-batch/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n#      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n        # Kafka 自定义 Binding 配置项，对应 KafkaBindingProperties Map\n        bindings:\n          demo01-output:\n            # Kafka Producer 配置项，对应 KafkaProducerProperties 类\n            producer:\n              batch-timeout: 30000 # 批处理延迟时间上限。这里配置为 30 * 1000 ms 过后，不管是否消息数量是否到达 batch-size 或者消息大小到达 buffer-memory 后，都直接发送一次请求\n              buffer-size: 33554432 # 每次批量发送消息的最大内存\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-11-spring-cloud-stream-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-11-sc-stream-kafka-producer-demo</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-demo/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-demo/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.controller;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.Demo01Message;\nimport cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send\")\n    public boolean send() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .build();\n        // 发送消息\n        boolean result = mySource.demo01Output().send(springMessage);\n        logger.info(\"[send][发送编号：[{}] 发送成功]\", message.getId());\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-demo/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-demo/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/message/MySource.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-demo/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n#      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n        # Kafka 自定义 Binding 配置项，对应 KafkaBindingProperties Map\n        bindings:\n          demo01-output:\n            # Kafka Producer 配置项，对应 KafkaProducerProperties 类\n            producer:\n              sync: true # 是否同步发送消息，默认为 false 异步。\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-partitioning/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-11-spring-cloud-stream-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-11-sc-stream-kafka-producer-partitioning</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-partitioning/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-partitioning/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.controller;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.Demo01Message;\nimport cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send_orderly\")\n    public boolean sendOrderly() {\n        // 发送 3 条相同 id 的消息\n        int id = new Random().nextInt();\n        for (int i = 0; i < 3; i++) {\n            // 创建 Message\n            Demo01Message message = new Demo01Message().setId(id);\n            // 创建 Spring Message 对象\n            Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                    .build();\n            // 发送消息\n            mySource.demo01Output().send(springMessage);\n        }\n        return true;\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-partitioning/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-partitioning/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/message/MySource.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-partitioning/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n#      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          # Producer 配置项，对应 ProducerProperties 类\n          producer:\n            partition-key-expression: payload['id'] # 分区 key 表达式。该表达式基于 Spring EL，从消息中获得分区 key。\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n        # Kafka 自定义 Binding 配置项，对应 KafkaBindingProperties Map\n        bindings:\n          demo01-output:\n            # Kafka Producer 配置项，对应 KafkaProducerProperties 类\n            producer:\n              sync: true # 是否同步发送消息，默认为 false 异步。\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-transaction/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-11-spring-cloud-stream-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-11-sc-stream-kafka-producer-transaction</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-transaction/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-transaction/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/config/TransactionConfig.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.config;\n\nimport org.springframework.cloud.stream.binder.BinderFactory;\nimport org.springframework.cloud.stream.binder.kafka.KafkaMessageChannelBinder;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.kafka.core.ProducerFactory;\nimport org.springframework.kafka.transaction.KafkaTransactionManager;\nimport org.springframework.messaging.MessageChannel;\nimport org.springframework.transaction.PlatformTransactionManager;\nimport org.springframework.transaction.annotation.EnableTransactionManagement;\n\n@Configuration\n@EnableTransactionManagement\npublic class TransactionConfig {\n\n    @Bean\n    public PlatformTransactionManager transactionManager(BinderFactory binders) {\n        // 获得 Kafka ProducerFactory 对象\n        ProducerFactory<byte[], byte[]> pf = ((KafkaMessageChannelBinder) binders.getBinder(null,\n                MessageChannel.class)).getTransactionalProducerFactory();\n        // 创建 KafkaTransactionManager 事务管理器\n        assert pf != null;\n        return new KafkaTransactionManager<>(pf);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-transaction/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.controller;\n\nimport cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.Demo01Message;\nimport cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message.MySource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @Transactional\n    @GetMapping(\"/send_transaction\")\n    public void sendTransaction() throws InterruptedException {\n        // 创建 Message\n        int id = new Random().nextInt();\n        Demo01Message message = new Demo01Message()\n                .setId(id);\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .build();\n        // 发送消息\n        mySource.demo01Output().send(springMessage);\n        logger.info(\"[send_transaction][发送编号：[{}] 发送成功]\", id);\n\n        // <X> 等待\n        Thread.sleep(10 * 1000L);\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-transaction/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-transaction/src/main/java/cn/iocoder/springcloud/labx11/kafkademo/kafkademo/message/MySource.java",
    "content": "package cn.iocoder.springcloud.labx11.kafkademo.kafkademo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/labx-11-sc-stream-kafka-producer-transaction/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n#      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n          transaction:\n            transaction-id-prefix: demo. # 事务编号前缀\n            producer:\n              configuration:\n                retries: 1 # 发送失败时，重试发送的次数\n                acks: all # 0-不应答。1-leader 应答。all-所有 leader 和 follower 应答。\n        # Kafka 自定义 Binding 配置项，对应 KafkaBindingProperties Map\n        bindings:\n          demo01-output:\n            # Kafka Producer 配置项，对应 KafkaProducerProperties 类\n            producer:\n              sync: true # 是否同步发送消息，默认为 false 异步。\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-11-spring-cloud-stream-kafka</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-11-sc-stream-kafka-producer-demo</module>\n        <module>labx-11-sc-stream-kafka-consumer-demo</module>\n\n        <!--搭配 <module>labx-11-sc-stream-kafka-producer-demo</module> 作为生产者-->\n        <module>labx-11-sc-stream-kafka-consumer-retry</module>\n\n        <!--搭配 <module>labx-11-sc-stream-kafka-producer-demo</module> 作为生产者-->\n        <module>labx-11-sc-stream-kafka-consumer-error-handler</module>\n\n        <!--搭配 <module>labx-11-sc-stream-kafka-producer-demo</module> 作为生产者-->\n        <module>labx-11-sc-stream-kafka-consumer-broadcasting</module>\n\n        <!--搭配 <module>labx-11-sc-stream-kafka-producer-demo</module> 作为生产者-->\n        <module>labx-11-sc-stream-kafka-consumer-concurrency</module>\n\n        <module>labx-11-sc-stream-kafka-producer-partitioning</module>\n        <!--搭配 <module>labx-11-sc-stream-kafka-consumer-concurrency</module> 作为消费者-->\n        <module>labx-11-sc-stream-kafka-consumer-partitioning</module> <!-- 暂时没用到 -->\n\n        <!--搭配 <module>labx-11-sc-stream-kafka-producer-demo</module> 作为生产者-->\n        <module>labx-11-sc-stream-kafka-consumer-filter</module>\n\n        <module>labx-11-sc-stream-kafka-producer-transaction</module>\n        <module>labx-11-sc-stream-kafka-consumer-transaction</module>\n\n        <!--搭配 <module>labx-11-sc-stream-kafka-producer-demo</module> 作为生产者-->\n        <module>labx-11-sc-stream-kafka-consumer-ack</module>\n\n        <module>labx-11-sc-stream-kafka-producer-batch</module>\n        <!--搭配 <module>labx-11-sc-stream-kafka-consumer-demo</module> 作为消费者-->\n\n        <!--搭配 <module>labx-11-sc-stream-kafka-producer-demo</module> 作为生产者-->\n        <module>labx-11-sc-stream-kafka-consumer-batch</module>\n\n        <module>labx-11-sc-stream-kafka-producer-actuator</module>\n        <module>labx-11-sc-stream-kafka-consumer-actuator</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-11-spring-cloud-stream-kafka/《芋道 Spring Cloud 消息队列 Kafka 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/Kafka/?github>\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-server-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-12-spring-cloud-config</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-12-sc-config-server-demo</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-config-server</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-server-demo/src/main/java/cn/iocoder/springcloud/labx12/configserverdemo/ConfigServerApplication.java",
    "content": "package cn.iocoder.springcloud.labx12.configserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.config.server.EnableConfigServer;\n\n@SpringBootApplication\n@EnableConfigServer\npublic class ConfigServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConfigServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-server-demo/src/main/resources/application.yml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: demo-config-server\n  profiles:\n    active: native # TODO 采用的方案\n  cloud:\n    config:\n      server:\n        native:\n          search-locations: classpath:/shared\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-server-demo/src/main/resources/shared/user-application.yml",
    "content": "order:\n  pay-timeout-seconds: 60 # 订单支付超时时长，单位：秒。\n  create-frequency-seconds: 120 # 订单创建频率，单位：秒\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-server-git/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-12-spring-cloud-config</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-12-sc-config-server-git</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Config Server 相关依赖，实现配置中心的服务端，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-config-server</artifactId>\n        </dependency>\n    </dependencies>\n\n<!--    <build>-->\n<!--        <resources>-->\n<!--            <resource>-->\n<!--                <directory>src/main/resources</directory>-->\n<!--                <filtering>false</filtering>-->\n<!--                <includes>-->\n<!--                    <include>*.keystore</include>-->\n<!--                    <include>*.yml</include>-->\n<!--                </includes>-->\n<!--            </resource>-->\n<!--        </resources>-->\n<!--    </build>-->\n\n    <build>\n        <resources>\n            <resource>\n                <directory>src/main/resource</directory>\n                <includes>\n                    <include>**/*.yml</include> <!-- 将 .yml 后缀包含进去 -->\n                    <include>**/*.jks</include> <!-- 将 .jks 后缀包含进去 -->\n                </includes>\n                <filtering>false</filtering> <!-- 不进行过滤 -->\n            </resource>\n        </resources>\n    </build>\n\n</project>\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-server-git/src/main/java/cn/iocoder/springcloud/labx12/configserverdemo/ConfigServerApplication.java",
    "content": "package cn.iocoder.springcloud.labx12.configserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.config.server.EnableConfigServer;\n\n@SpringBootApplication\n@EnableConfigServer\npublic class ConfigServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConfigServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-server-git/src/main/resources/application.yml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: demo-config-server\n\n  profiles:\n    active: git # 使用的 Spring Cloud Config Server 的存储器方案\n  cloud:\n    config:\n      server:\n        # Spring Cloud Config Server 的 Git 存储器的配置项，对应 MultipleJGitEnvironmentProperties 类\n        git:\n          uri: https://github.com/YunaiV/demo-config-server.git # Git 仓库地址\n          search-paths: / # 读取文件的根地址\n          default-label: master # 使用的默认分支，默认为 master\n#          username: ${CODING_USERNAME} # 账号\n#          password: ${CODING_PASSWORD} # 密码\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-server-git/src/main/resources/bootstrap.yml",
    "content": "# 加密配置项，对应 KeyProperties 类\nencrypt:\n  # 对称加密\n#  key: yudaoyuanma # 对称加密 key\n#  salt: dfaad7761f0729b35ec3b3543604eda7 # 对称加密 salt，默认为 \"deadbeef\"，必须是 16 进制\n\n  # 非对应加密\n  key-store:\n    location: classpath:/configserver.jks # jks 文件所在的路径\n    password: buzhidao # 对应 storepass 参数\n    alias: mytestkey # 对应 alias 参数\n    secret: nicainicai # 对应 keypass 参数\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-server-git-auto-refresh-by-bus/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-12-spring-cloud-config</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-12-sc-config-server-git-auto-refresh-by-bus</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-config-server</artifactId>\n        </dependency>\n\n        <!-- 引入基于 RabbitMQ 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-amqp</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Config Monitor 依赖，提供 `/monitor` 接口，用于刷新配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-config-monitor</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-server-git-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx12/configserverdemo/ConfigServerApplication.java",
    "content": "package cn.iocoder.springcloud.labx12.configserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.config.server.EnableConfigServer;\n\n@SpringBootApplication\n@EnableConfigServer\npublic class ConfigServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConfigServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-server-git-auto-refresh-by-bus/src/main/resources/application.yml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: demo-config-server\n\n  profiles:\n    active: git # 使用的 Spring Cloud Config Server 的存储器方案\n  # Spring Cloud Config 相关配置项\n  cloud:\n    config:\n      server:\n        # Spring Cloud Config Server 的 Git 存储器的配置项，对应 MultipleJGitEnvironmentProperties 类\n        git:\n          uri: https://github.com/YunaiV/demo-config-server.git # Git 仓库地址\n          search-paths: / # 读取文件的根地址\n          default-label: master # 使用的默认分支，默认为 master\n  #          username: ${CODING_USERNAME} # 账号\n  #          password: ${CODING_PASSWORD} # 密码\n\n  # RabbitMQ 相关配置项\n  rabbitmq:\n    host: localhost\n    port: 5672\n    username: guest\n    password: guest\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-server-git-nacos/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-12-spring-cloud-config</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-12-sc-config-server-git-nacos</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Config Server 相关依赖，实现配置中心的服务端，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-config-server</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-server-git-nacos/src/main/java/cn/iocoder/springcloud/labx12/configserverdemo/ConfigServerApplication.java",
    "content": "package cn.iocoder.springcloud.labx12.configserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.config.server.EnableConfigServer;\n\n@SpringBootApplication\n@EnableConfigServer\npublic class ConfigServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConfigServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-server-git-nacos/src/main/resources/application.yml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: demo-config-server\n\n  profiles:\n    active: git # 使用的 Spring Cloud Config Server 的存储器方案\n\n  # Spring Cloud Config 相关配置项\n  cloud:\n    config:\n      server:\n        # Spring Cloud Config Server 的 Git 存储器的配置项，对应 MultipleJGitEnvironmentProperties 类\n        git:\n          uri: https://github.com/YunaiV/demo-config-server.git # Git 仓库地址\n          search-paths: / # 读取文件的根地址\n          default-label: master # 使用的默认分支，默认为 master\n#          username: ${CODING_USERNAME} # 账号\n#          password: ${CODING_PASSWORD} # 密码\n\n    # Spring Cloud Nacos Discovery 相关配置项\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-12-spring-cloud-config</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-12-sc-config-user-application</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Config Client 相关依赖，作为配置中心的客户端，并实现自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-config</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application/src/main/java/cn/iocoder/springcloud/labx12/userapplication/UserApplication.java",
    "content": "package cn.iocoder.springcloud.labx12.userapplication;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application/src/main/java/cn/iocoder/springcloud/labx12/userapplication/config/OrderProperties.java",
    "content": "package cn.iocoder.springcloud.labx12.userapplication.config;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application/src/main/java/cn/iocoder/springcloud/labx12/userapplication/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx12.userapplication.controller;\n\nimport cn.iocoder.springcloud.labx12.userapplication.config.OrderProperties;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private OrderProperties orderProperties;\n\n    /**\n     * 测试 @ConfigurationProperties 注解的配置属性类\n     */\n    @GetMapping(\"/test01\")\n    public OrderProperties test01() {\n        return orderProperties;\n    }\n\n    @Value(value = \"${order.pay-timeout-seconds}\")\n    private Integer payTimeoutSeconds;\n    @Value(value = \"${order.create-frequency-seconds}\")\n    private Integer createFrequencySeconds;\n\n    /**\n     * 测试 @Value 注解的属性\n     */\n    @GetMapping(\"/test02\")\n    public Map<String, Object> test02() {\n        Map<String, Object> result = new HashMap<>();\n        result.put(\"payTimeoutSeconds\", payTimeoutSeconds);\n        result.put(\"createFrequencySeconds\", createFrequencySeconds);\n        return result;\n    }\n\n    @Value(value = \"${xx-password:''}\")\n    private String xxPassword;\n\n    @GetMapping(\"/xx_password\")\n    public String xxPassword() {\n        return xxPassword;\n    }\n\n    @Value(value = \"${yy-password:''}\")\n    private String yyPassword;\n\n    @GetMapping(\"/yy_password\")\n    public String yyPassword() {\n        return yyPassword;\n    }\n\n}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application/src/main/resources/bootstrap.yml",
    "content": "spring:\n  application:\n    name: user-application\n\n  cloud:\n    # Spring Cloud Config Client 配置项，对应 ConfigClientProperties 类\n    config:\n      uri: http://127.0.0.1:8888 # Spring Cloud Config Server 的地址\n      name: ${spring.application.name} # 读取的配置文件的名字，默认为 ${spring.application.name}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-12-spring-cloud-config</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-12-sc-config-user-application-auto-refresh-by-actuator</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Config Client 相关依赖，作为配置中心的客户端，并实现自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-config</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-actuator/src/main/java/cn/iocoder/springcloud/labx12/userapplication/UserApplication.java",
    "content": "package cn.iocoder.springcloud.labx12.userapplication;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-actuator/src/main/java/cn/iocoder/springcloud/labx12/userapplication/config/OrderProperties.java",
    "content": "package cn.iocoder.springcloud.labx12.userapplication.config;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n//@NacosConfigurationProperties(prefix = \"order\", dataId = \"${nacos.config.data-id}\", type = ConfigType.YAML)\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n//    /**\n//     * 配置描述\n//     */\n//    private String desc;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n//    public String getDesc() {\n//        return desc;\n//    }\n//\n//    public OrderProperties setDesc(String desc) {\n//        this.desc = desc;\n//        return this;\n//    }\n\n}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-actuator/src/main/java/cn/iocoder/springcloud/labx12/userapplication/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx12.userapplication.controller;\n\nimport cn.iocoder.springcloud.labx12.userapplication.config.OrderProperties;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.cloud.context.config.annotation.RefreshScope;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/demo\")\n@RefreshScope\npublic class DemoController {\n\n    @Autowired\n    private OrderProperties orderProperties;\n\n    /**\n     * 测试 @ConfigurationProperties 注解的配置属性类\n     */\n    @GetMapping(\"/test01\")\n    public OrderProperties test01() {\n        return orderProperties;\n    }\n\n    @Value(value = \"${order.pay-timeout-seconds}\") // @NacosValue(value = \"${order.pay-timeout-seconds}\")\n    private Integer payTimeoutSeconds;\n    @Value(value = \"${order.create-frequency-seconds}\") // @NacosValue(value = \"${order.create-frequency-seconds}\")\n    private Integer createFrequencySeconds;\n\n    /**\n     * 测试 @Value 注解的属性\n     */\n    @GetMapping(\"/test02\")\n    public Map<String, Object> test02() {\n        Map<String, Object> result = new HashMap<>();\n        result.put(\"payTimeoutSeconds\", payTimeoutSeconds);\n        result.put(\"createFrequencySeconds\", createFrequencySeconds);\n        return result;\n    }\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/logger\")\n    public void logger() {\n        logger.debug(\"[logger][测试一下]\");\n    }\n\n}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-actuator/src/main/java/cn/iocoder/springcloud/labx12/userapplication/listener/DemoEnvironmentChangeListener.java",
    "content": "package cn.iocoder.springcloud.labx12.userapplication.listener;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.context.environment.EnvironmentChangeEvent;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.core.env.ConfigurableEnvironment;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class DemoEnvironmentChangeListener implements ApplicationListener<EnvironmentChangeEvent> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private ConfigurableEnvironment environment;\n\n    @Override\n    public void onApplicationEvent(EnvironmentChangeEvent event) {\n        for (String key : event.getKeys()) {\n            logger.info(\"[onApplicationEvent][key({}) 最新 value 为 {}]\", key, environment.getProperty(key));\n        }\n    }\n\n}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-actuator/src/main/resources/application.yml",
    "content": "management:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      exposure:\n        include: refresh # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-actuator/src/main/resources/bootstrap.yml",
    "content": "spring:\n  application:\n    name: user-application\n  cloud:\n    config:\n      uri: http://127.0.0.1:8888\n      name: ${spring.application.name}\n      fail-fast: true # TODO\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-bus/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-12-spring-cloud-config</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-12-sc-config-user-application-auto-refresh-by-bus</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Config Client 相关依赖，作为配置中心的客户端，并实现自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-config</artifactId>\n        </dependency>\n\n        <!-- 引入基于 RabbitMQ 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-amqp</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx12/userapplication/UserApplication.java",
    "content": "package cn.iocoder.springcloud.labx12.userapplication;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx12/userapplication/config/OrderProperties.java",
    "content": "package cn.iocoder.springcloud.labx12.userapplication.config;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx12/userapplication/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx12.userapplication.controller;\n\nimport cn.iocoder.springcloud.labx12.userapplication.config.OrderProperties;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.cloud.context.config.annotation.RefreshScope;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/demo\")\n@RefreshScope\npublic class DemoController {\n\n    @Autowired\n    private OrderProperties orderProperties;\n\n    /**\n     * 测试 @ConfigurationProperties 注解的配置属性类\n     */\n    @GetMapping(\"/test01\")\n    public OrderProperties test01() {\n        return orderProperties;\n    }\n\n    @Value(value = \"${order.pay-timeout-seconds}\")\n    private Integer payTimeoutSeconds;\n    @Value(value = \"${order.create-frequency-seconds}\")\n    private Integer createFrequencySeconds;\n\n    /**\n     * 测试 @Value 注解的属性\n     */\n    @GetMapping(\"/test02\")\n    public Map<String, Object> test02() {\n        Map<String, Object> result = new HashMap<>();\n        result.put(\"payTimeoutSeconds\", payTimeoutSeconds);\n        result.put(\"createFrequencySeconds\", createFrequencySeconds);\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-bus/src/main/resources/application.yml",
    "content": "spring:\n  # RabbitMQ 相关配置项\n  rabbitmq:\n    host: localhost\n    port: 5672\n    username: guest\n    password: guest\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application-auto-refresh-by-bus/src/main/resources/bootstrap.yml",
    "content": "spring:\n  application:\n    name: user-application\n\n  cloud:\n    # Spring Cloud Config Client 配置项，对应 ConfigClientProperties 类\n    config:\n      uri: http://127.0.0.1:8888 # Spring Cloud Config Server 的地址\n      name: ${spring.application.name} # 读取的配置文件的名字，默认为 ${spring.application.name}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application-nacos/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-12-spring-cloud-config</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-12-sc-config-user-application-nacos</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Config Client 相关依赖，作为配置中心的客户端，并实现自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-config</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application-nacos/src/main/java/cn/iocoder/springcloud/labx12/userapplication/UserApplication.java",
    "content": "package cn.iocoder.springcloud.labx12.userapplication;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application-nacos/src/main/java/cn/iocoder/springcloud/labx12/userapplication/config/OrderProperties.java",
    "content": "package cn.iocoder.springcloud.labx12.userapplication.config;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application-nacos/src/main/java/cn/iocoder/springcloud/labx12/userapplication/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx12.userapplication.controller;\n\nimport cn.iocoder.springcloud.labx12.userapplication.config.OrderProperties;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private OrderProperties orderProperties;\n\n    /**\n     * 测试 @ConfigurationProperties 注解的配置属性类\n     */\n    @GetMapping(\"/test01\")\n    public OrderProperties test01() {\n        return orderProperties;\n    }\n\n    @Value(value = \"${order.pay-timeout-seconds}\")\n    private Integer payTimeoutSeconds;\n    @Value(value = \"${order.create-frequency-seconds}\")\n    private Integer createFrequencySeconds;\n\n    /**\n     * 测试 @Value 注解的属性\n     */\n    @GetMapping(\"/test02\")\n    public Map<String, Object> test02() {\n        Map<String, Object> result = new HashMap<>();\n        result.put(\"payTimeoutSeconds\", payTimeoutSeconds);\n        result.put(\"createFrequencySeconds\", createFrequencySeconds);\n        return result;\n    }\n\n    @Value(value = \"${xx-password:''}\")\n    private String xxPassword;\n\n    @GetMapping(\"/xx_password\")\n    public String xxPassword() {\n        return xxPassword;\n    }\n\n    @Value(value = \"${yy-password:''}\")\n    private String yyPassword;\n\n    @GetMapping(\"/yy_password\")\n    public String yyPassword() {\n        return yyPassword;\n    }\n\n}\n"
  },
  {
    "path": "labx-12-spring-cloud-config/labx-12-sc-config-user-application-nacos/src/main/resources/bootstrap.yml",
    "content": "spring:\n  application:\n    name: user-application\n\n  cloud:\n    # Spring Cloud Config Client 配置项，对应 ConfigClientProperties 类\n    config:\n      name: ${spring.application.name} # 读取的配置文件的名字，默认为 ${spring.application.name}\n      discovery:\n        enabled: true # 是否使用注册发现，获取配置中心的地址，默认为 false\n        service-id: demo-config-server # 配置中心的服务名\n\n    # Spring Cloud Nacos Discovery 相关配置项\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。\n"
  },
  {
    "path": "labx-12-spring-cloud-config/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-12-spring-cloud-config</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-12-sc-config-server-demo</module>\n        <module>labx-12-sc-config-user-application</module>\n\n        <module>labx-12-sc-config-server-git</module>\n        <!-- 使用 <module>labx-12-sc-config-user-application</module> 作为 Client-->\n\n        <!-- 使用 <module>labx-12-sc-config-server-demo</module> 作为 Server-->\n        <module>labx-12-sc-config-user-application-auto-refresh-by-actuator</module>\n\n        <module>labx-12-sc-config-server-git-auto-refresh-by-bus</module>\n        <module>labx-12-sc-config-user-application-auto-refresh-by-bus</module>\n\n        <module>labx-12-sc-config-server-git-nacos</module>\n        <module>labx-12-sc-config-user-application-nacos</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-12-spring-cloud-config/《芋道 Spring Cloud 配置中心 Spring Cloud Config 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/Spring-Cloud-Config/?github>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-elasticsearch/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-db-elasticsearch</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 自动化配置 Spring Data Elasticsearch -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Sleuth + Zipkin 相关依赖，实现对它们的自动配置，从而实现链路追踪 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zipkin</artifactId>\n        </dependency>\n\n        <!--  Brave 对 Opentracing 的实现 -->\n        <dependency>\n            <groupId>io.opentracing.brave</groupId>\n            <artifactId>brave-opentracing</artifactId>\n            <version>0.35.0</version>\n        </dependency>\n\n        <!-- Opentracing 对 Elasticsearch 的支持 -->\n        <dependency>\n            <groupId>io.opentracing.contrib</groupId>\n            <artifactId>opentracing-elasticsearch6-client</artifactId>\n            <version>0.1.6</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-elasticsearch/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/UserServiceApplication.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-elasticsearch/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/config/SleuthConfiguration.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo.config;\n\nimport cn.iocoder.springcloud.labx13.springmvcdemo.spring.TracingTransportClientFactoryBean;\nimport io.opentracing.Tracer;\nimport org.elasticsearch.client.transport.TransportClient;\nimport org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.util.Properties;\n\n@Configuration\npublic class SleuthConfiguration {\n\n    // ==================== Elasticsearch 相关 ====================\n\n    @Bean\n    public TransportClient elasticsearchClient(Tracer tracer, ElasticsearchProperties elasticsearchProperties) throws Exception {\n        // 创建 TracingTransportClientFactoryBean 对象\n        TracingTransportClientFactoryBean factory = new TracingTransportClientFactoryBean(tracer);\n        // 设置其属性\n        factory.setClusterNodes(elasticsearchProperties.getClusterNodes());\n        factory.setProperties(this.createElasticsearch(elasticsearchProperties));\n        // 创建 TransportClient 对象，并返回\n        factory.afterPropertiesSet();\n        return factory.getObject();\n    }\n\n    private Properties createElasticsearch(ElasticsearchProperties elasticsearchProperties) {\n        Properties properties = new Properties();\n        properties.put(\"cluster.name\", elasticsearchProperties.getClusterName());\n        properties.putAll(elasticsearchProperties.getProperties());\n        return properties;\n    }\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-elasticsearch/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/controller/UserController.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo.controller;\n\nimport cn.iocoder.springcloud.labx13.springmvcdemo.dataobject.ESUserDO;\nimport cn.iocoder.springcloud.labx13.springmvcdemo.repository.ESUserRepository;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Autowired\n    private ESUserRepository userRepository;\n\n    @GetMapping(\"/get\")\n    public String get(@RequestParam(\"id\") Integer id) {\n        this.findById(id);\n        return \"success\";\n    }\n\n    public ESUserDO findById(Integer id) {\n        return userRepository.findById(id).orElse(null);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-elasticsearch/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/dataobject/ESUserDO.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo.dataobject;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.elasticsearch.annotations.Document;\n\nimport java.util.Date;\n\n@Document(indexName = \"user\", // 索引名\n        type = \"user\", // 类型。未来的版本即将废弃\n        shards = 1, // 默认索引分区数\n        replicas = 0, // 每个分区的备份数\n        refreshInterval = \"-1\" // 刷新间隔\n)\npublic class ESUserDO {\n\n    @Id\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                \", createTime=\" + createTime +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-elasticsearch/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/repository/ESUserRepository.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo.repository;\n\nimport cn.iocoder.springcloud.labx13.springmvcdemo.dataobject.ESUserDO;\nimport org.springframework.data.elasticsearch.repository.ElasticsearchRepository;\n\npublic interface ESUserRepository extends ElasticsearchRepository<ESUserDO, Integer> {\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-elasticsearch/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/spring/ClusterNodes.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo.spring;\n\nimport org.elasticsearch.common.transport.TransportAddress;\nimport org.springframework.data.util.Streamable;\nimport org.springframework.util.Assert;\nimport org.springframework.util.StringUtils;\n\nimport java.net.InetAddress;\nimport java.net.UnknownHostException;\nimport java.util.Arrays;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.stream.Collectors;\n\nclass ClusterNodes implements Streamable<TransportAddress> {\n\n    public static ClusterNodes DEFAULT = ClusterNodes.of(\"127.0.0.1:9300\");\n\n    private static final String COLON = \":\";\n    private static final String COMMA = \",\";\n\n    private final List<TransportAddress> clusterNodes;\n\n    /**\n     * Creates a new {@link ClusterNodes} by parsing the given source.\n     *\n     * @param source must not be {@literal null} or empty.\n     */\n    private ClusterNodes(String source) {\n\n        Assert.hasText(source, \"Cluster nodes source must not be null or empty!\");\n\n        String[] nodes = StringUtils.delimitedListToStringArray(source, COMMA);\n\n        this.clusterNodes = Arrays.stream(nodes).map(node -> {\n\n            String[] segments = StringUtils.delimitedListToStringArray(node, COLON);\n\n            Assert.isTrue(segments.length == 2,\n                    () -> String.format(\"Invalid cluster node %s in %s! Must be in the format host:port!\", node, source));\n\n            String host = segments[0].trim();\n            String port = segments[1].trim();\n\n            Assert.hasText(host, () -> String.format(\"No host name given cluster node %s!\", node));\n            Assert.hasText(port, () -> String.format(\"No port given in cluster node %s!\", node));\n\n            return new TransportAddress(toInetAddress(host), Integer.valueOf(port));\n\n        }).collect(Collectors.toList());\n    }\n\n    /**\n     * Creates a new {@link ClusterNodes} by parsing the given source. The expected format is a comma separated list of\n     * host-port-combinations separated by a colon: {@code host:port,host:port,…}.\n     *\n     * @param source must not be {@literal null} or empty.\n     * @return\n     */\n    public static ClusterNodes of(String source) {\n        return new ClusterNodes(source);\n    }\n\n    /*\n     * (non-Javadoc)\n     * @see java.lang.Iterable#iterator()\n     */\n    @Override\n    public Iterator<TransportAddress> iterator() {\n        return clusterNodes.iterator();\n    }\n\n    private static InetAddress toInetAddress(String host) {\n\n        try {\n            return InetAddress.getByName(host);\n        } catch (UnknownHostException o_O) {\n            throw new IllegalArgumentException(o_O);\n        }\n    }\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-elasticsearch/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/spring/TracingTransportClientFactoryBean.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo.spring;\n\nimport io.opentracing.Tracer;\nimport io.opentracing.contrib.elasticsearch6.TracingPreBuiltTransportClient;\nimport org.elasticsearch.client.transport.TransportClient;\nimport org.elasticsearch.common.settings.Settings;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.DisposableBean;\nimport org.springframework.beans.factory.FactoryBean;\nimport org.springframework.beans.factory.InitializingBean;\nimport org.springframework.data.elasticsearch.client.TransportClientFactoryBean;\n\nimport java.util.Properties;\n\n/**\n * 参考 {@link TransportClientFactoryBean} 来实现。\n */\npublic class TracingTransportClientFactoryBean implements FactoryBean<TransportClient>, InitializingBean, DisposableBean {\n\n    private static final Logger logger = LoggerFactory.getLogger(TransportClientFactoryBean.class);\n    private ClusterNodes clusterNodes = ClusterNodes.of(\"127.0.0.1:9300\");\n    private String clusterName = \"elasticsearch\";\n    private Boolean clientTransportSniff = true;\n    private Boolean clientIgnoreClusterName = Boolean.FALSE;\n    private String clientPingTimeout = \"5s\";\n    private String clientNodesSamplerInterval = \"5s\";\n    private TransportClient client;\n    private Properties properties;\n\n    private Tracer tracer;\n\n    public TracingTransportClientFactoryBean(Tracer tracer) {\n        this.tracer = tracer;\n    }\n\n    @Override\n    public void destroy() throws Exception {\n        try {\n            logger.info(\"Closing elasticSearch  client\");\n            if (client != null) {\n                client.close();\n            }\n        } catch (final Exception e) {\n            logger.error(\"Error closing ElasticSearch client: \", e);\n        }\n    }\n\n    @Override\n    public TransportClient getObject() throws Exception {\n        return client;\n    }\n\n    @Override\n    public Class<TransportClient> getObjectType() {\n        return TransportClient.class;\n    }\n\n    @Override\n    public boolean isSingleton() {\n        return true;\n    }\n\n    @Override\n    public void afterPropertiesSet() throws Exception {\n        buildClient();\n    }\n\n    protected void buildClient() throws Exception {\n        // 创建可追踪的 TracingPreBuiltTransportClient\n        client = new TracingPreBuiltTransportClient(tracer, settings());\n\n        clusterNodes.stream() //\n                .peek(it -> logger.info(\"Adding transport node : \" + it.toString())) //\n                .forEach(client::addTransportAddress);\n\n        client.connectedNodes();\n    }\n\n    private Settings settings() {\n        if (properties != null) {\n            Settings.Builder builder = Settings.builder();\n\n            properties.forEach((key, value) -> {\n                builder.put(key.toString(), value.toString());\n            });\n\n            return builder.build();\n        }\n        return Settings.builder()\n                .put(\"cluster.name\", clusterName)\n                .put(\"client.transport.sniff\", clientTransportSniff)\n                .put(\"client.transport.ignore_cluster_name\", clientIgnoreClusterName)\n                .put(\"client.transport.ping_timeout\", clientPingTimeout)\n                .put(\"client.transport.nodes_sampler_interval\", clientNodesSamplerInterval)\n                .build();\n    }\n\n    public void setClusterNodes(String clusterNodes) {\n        this.clusterNodes = ClusterNodes.of(clusterNodes);\n    }\n\n    public void setClusterName(String clusterName) {\n        this.clusterName = clusterName;\n    }\n\n    public void setClientTransportSniff(Boolean clientTransportSniff) {\n        this.clientTransportSniff = clientTransportSniff;\n    }\n\n    public String getClientNodesSamplerInterval() {\n        return clientNodesSamplerInterval;\n    }\n\n    public void setClientNodesSamplerInterval(String clientNodesSamplerInterval) {\n        this.clientNodesSamplerInterval = clientNodesSamplerInterval;\n    }\n\n    public String getClientPingTimeout() {\n        return clientPingTimeout;\n    }\n\n    public void setClientPingTimeout(String clientPingTimeout) {\n        this.clientPingTimeout = clientPingTimeout;\n    }\n\n    public Boolean getClientIgnoreClusterName() {\n        return clientIgnoreClusterName;\n    }\n\n    public void setClientIgnoreClusterName(Boolean clientIgnoreClusterName) {\n        this.clientIgnoreClusterName = clientIgnoreClusterName;\n    }\n\n    public void setProperties(Properties properties) {\n        this.properties = properties;\n    }\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-elasticsearch/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: user-service # 服务名\n\n  # Zipkin 配置项，对应 ZipkinProperties 类\n  zipkin:\n    base-url: http://127.0.0.1:9411 # Zipkin 服务的地址\n\n  # Spring Cloud Sleuth 配置项\n  sleuth:\n    # Spring Cloud Sleuth 针对 Web 组件的配置项，例如说 SpringMVC\n    web:\n      enabled: true # 是否开启，默认为 true\n\n  data:\n    # Elasticsearch 配置项\n    elasticsearch:\n      cluster-name: elasticsearch # 集群名\n      cluster-nodes: 127.0.0.1:9300 # 集群节点\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-mongodb/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-db-mongodb</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 自动化配置 Spring Data Mongodb -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-mongodb</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Sleuth + Zipkin 相关依赖，实现对它们的自动配置，从而实现链路追踪 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zipkin</artifactId>\n        </dependency>\n\n        <!--  Brave 对 Opentracing 的实现 -->\n        <dependency>\n            <groupId>io.opentracing.brave</groupId>\n            <artifactId>brave-opentracing</artifactId>\n            <version>0.35.0</version>\n        </dependency>\n\n        <!-- Opentracing 对 MongoDB 的支持 -->\n        <dependency>\n            <groupId>io.opentracing.contrib</groupId>\n            <artifactId>opentracing-mongo-driver</artifactId>\n            <version>0.1.5</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-mongodb/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/UserServiceApplication.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-mongodb/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/config/SleuthConfiguration.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo.config;\n\nimport com.mongodb.MongoClientOptions;\nimport io.opentracing.Tracer;\nimport io.opentracing.contrib.mongo.common.TracingCommandListener;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class SleuthConfiguration {\n\n    // ==================== MongoDB 相关 ====================\n\n    @Bean\n    public MongoClientOptions mongoClientOptions(Tracer tracer) {\n        // 创建 TracingCommandListener 对象\n        TracingCommandListener listener = new TracingCommandListener.Builder(tracer).build();\n        // 创建 MongoClientOptions 对象，并设置监听器\n        return MongoClientOptions.builder().addCommandListener(listener).build();\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-mongodb/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/controller/UserController.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo.controller;\n\nimport cn.iocoder.springcloud.labx13.springmvcdemo.dataobject.UserDO;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.mongodb.core.MongoTemplate;\nimport org.springframework.data.mongodb.core.query.Criteria;\nimport org.springframework.data.mongodb.core.query.Query;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Autowired\n    private MongoTemplate mongoTemplate;\n\n    @GetMapping(\"/get\")\n    public String get(@RequestParam(\"id\") Integer id) {\n        this.findById(1);\n        return \"success\";\n    }\n\n    public UserDO findById(Integer id) {\n        return mongoTemplate.findOne(new Query(Criteria.where(\"_id\").is(id)), UserDO.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-mongodb/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/dataobject/UserDO.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo.dataobject;\n\nimport org.springframework.data.annotation.Id;\nimport org.springframework.data.mongodb.core.mapping.Document;\n\nimport java.util.Date;\n\n/**\n * 用户 DO\n */\n@Document(collection = \"User\")\npublic class UserDO {\n\n    @Id\n    private Integer id;\n    /**\n     * 账号\n     */\n    private String username;\n    /**\n     * 密码\n     */\n    private String password;\n    /**\n     * 创建时间\n     */\n    private Date createTime;\n\n    @Override\n    public String toString() {\n        return \"UserDO{\" +\n                \"id=\" + id +\n                \", username='\" + username + '\\'' +\n                \", password='\" + password + '\\'' +\n                \", createTime=\" + createTime +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-mongodb/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: user-service # 服务名\n\n  # Zipkin 配置项，对应 ZipkinProperties 类\n  zipkin:\n    base-url: http://127.0.0.1:9411 # Zipkin 服务的地址\n\n  # Spring Cloud Sleuth 配置项\n  sleuth:\n    # Spring Cloud Sleuth 针对 Web 组件的配置项，例如说 SpringMVC\n    web:\n      enabled: true # 是否开启，默认为 true\n\n  data:\n    # MongoDB 配置项，对应 MongoProperties 类\n    mongodb:\n      host: 127.0.0.1\n      port: 27017\n      database: yourdatabase\n      username: test01\n      password: password01\n      # 上述属性，也可以只配置 uri\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-mysql/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-db-mysql</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.46</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Sleuth + Zipkin 相关依赖，实现对它们的自动配置，从而实现链路追踪 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zipkin</artifactId>\n        </dependency>\n\n        <!-- Brave 对 MySQL 的支持 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-mysql</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-mysql/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/UserServiceApplication.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-mysql/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/controller/UserController.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jdbc.core.BeanPropertyRowMapper;\nimport org.springframework.jdbc.core.JdbcTemplate;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Autowired\n    private JdbcTemplate template;\n\n    @GetMapping(\"/get\")\n    public String get(@RequestParam(\"id\") Integer id) {\n        this.selectById(1);\n        return \"success\";\n    }\n\n    public Object selectById(Integer id) {\n        return template.queryForObject(\"SELECT id, username, password FROM t_user WHERE id = ?\",\n                new BeanPropertyRowMapper<>(Object.class), // 结果转换成对应的对象。Object 理论来说是 UserDO.class ，这里偷懒了。\n                id);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-mysql/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: user-service # 服务名\n\n  # Zipkin 配置项，对应 ZipkinProperties 类\n  zipkin:\n    base-url: http://127.0.0.1:9411 # Zipkin 服务的地址\n\n  # Spring Cloud Sleuth 配置项\n  sleuth:\n    # Spring Cloud Sleuth 针对 Web 组件的配置项，例如说 SpringMVC\n    web:\n      enabled: true # 是否开启，默认为 true\n\n  # datasource 数据源配置内容\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/lab-39-mysql?useSSL=false&useUnicode=true&characterEncoding=UTF-8&statementInterceptors=brave.mysql.TracingStatementInterceptor&zipkinServiceName=demo-db-mysql\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-redis/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-db-redis</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Data Redis 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-data-redis</artifactId>\n            <exclusions>\n                <!-- 去掉对 Lettuce 的依赖，因为 Spring Boot 优先使用 Lettuce 作为 Redis 客户端 -->\n                <exclusion>\n                    <groupId>io.lettuce</groupId>\n                    <artifactId>lettuce-core</artifactId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n        <!-- 引入 Jedis 的依赖，这样 Spring Boot 实现对 Jedis 的自动化配置 -->\n        <dependency>\n            <groupId>redis.clients</groupId>\n            <artifactId>jedis</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Sleuth + Zipkin 相关依赖，实现对它们的自动配置，从而实现链路追踪 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zipkin</artifactId>\n        </dependency>\n\n        <!--  Brave 对 Opentracing 的实现 -->\n        <dependency>\n            <groupId>io.opentracing.brave</groupId>\n            <artifactId>brave-opentracing</artifactId>\n            <version>0.35.0</version>\n        </dependency>\n\n        <!-- Opentracing 对 Redis 的支持 -->\n        <dependency>\n            <groupId>io.opentracing.contrib</groupId>\n            <artifactId>opentracing-redis-jedis3</artifactId>\n            <version>0.1.14</version>\n        </dependency>\n        <dependency>\n            <groupId>io.opentracing.contrib</groupId>\n            <artifactId>opentracing-redis-spring-data</artifactId>\n            <version>0.1.14</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-redis/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/UserServiceApplication.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-redis/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/config/SleuthConfiguration.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo.config;\n\nimport io.opentracing.Tracer;\nimport io.opentracing.contrib.redis.common.TracingConfiguration;\nimport io.opentracing.contrib.redis.spring.data.connection.TracingRedisConnectionFactory;\nimport org.springframework.boot.autoconfigure.data.redis.RedisProperties;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.redis.connection.RedisConnectionFactory;\nimport org.springframework.data.redis.connection.jedis.JedisConnectionFactory;\n\n@Configuration\npublic class SleuthConfiguration {\n\n    // ==================== Redis 相关 ====================\n    @Bean\n    public RedisConnectionFactory redisConnectionFactory(Tracer tracer, RedisProperties redisProperties) {\n        // 创建 JedisConnectionFactory 对象\n        RedisConnectionFactory connectionFactory = new JedisConnectionFactory();\n        // 创建 TracingConfiguration 对象\n        TracingConfiguration tracingConfiguration = new TracingConfiguration.Builder(tracer)\n                // 设置拓展 Tag ，设置 Redis 服务器地址。因为默认情况下，不会在操作 Redis 链路的 Span 上记录 Redis 服务器的地址，所以这里需要设置。\n                .extensionTag(\"Server Address\", redisProperties.getHost() + \":\" + redisProperties.getPort())\n                .build();\n        // 创建 TracingRedisConnectionFactory 对象\n        return new TracingRedisConnectionFactory(connectionFactory, tracingConfiguration);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-redis/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/controller/UserController.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo.controller;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.data.redis.core.StringRedisTemplate;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Autowired\n    private StringRedisTemplate redisTemplate;\n\n    @GetMapping(\"/get\")\n    public String get(@RequestParam(\"id\") Integer id) {\n        this.get(\"demo\");\n        return \"success\";\n    }\n\n    public void get(String key) {\n        redisTemplate.opsForValue().get(key);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-db-redis/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: user-service # 服务名\n\n  # Zipkin 配置项，对应 ZipkinProperties 类\n  zipkin:\n    base-url: http://127.0.0.1:9411 # Zipkin 服务的地址\n\n  # Spring Cloud Sleuth 配置项\n  sleuth:\n    # Spring Cloud Sleuth 针对 Web 组件的配置项，例如说 SpringMVC\n    web:\n      enabled: true # 是否开启，默认为 true\n\n  # 对应 RedisProperties 类\n  redis:\n    host: 127.0.0.1\n    port: 6379\n    password: # Redis 服务器密码，默认为空。生产中，一定要设置 Redis 密码！\n    database: 0 # Redis 数据库号，默认为 0 。\n    timeout: 0 # Redis 连接超时时间，单位：毫秒。\n    # 对应 RedisProperties.Jedis 内部类\n    jedis:\n      pool:\n        max-active: 8 # 连接池最大连接数，默认为 8 。使用负数表示没有限制。\n        max-idle: 8 # 默认连接数最小空闲的连接数，默认为 8 。使用负数表示没有限制。\n        min-idle: 0 # 默认连接池最小空闲的连接数，默认为 0 。允许设置 0 和 正数。\n        max-wait: -1 # 连接池最大阻塞等待时间，单位：毫秒。默认为 -1 ，表示不限制。\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13-sc-sleuth-dubbo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-dubbo-api</artifactId>\n\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-api/src/main/java/cn/iocoder/springcloud/labx13/api/UserService.java",
    "content": "package cn.iocoder.springcloud.labx13.api;\n\n/**\n * 用户服务 RPC Service 接口\n */\npublic interface UserService {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    String get(Integer id);\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-api/src/main/java/cn/iocoder/springcloud/labx13/package-info.java",
    "content": "package cn.iocoder.springcloud.labx13;\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13-sc-sleuth-dubbo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-dubbo-consumer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n    -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-13-sc-sleuth-dubbo-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Sleuth + Zipkin 相关依赖，实现对它们的自动配置，从而实现链路追踪 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zipkin</artifactId>\n        </dependency>\n\n        <!-- Brave 针对 Dubbo 的插件，实现链路追踪 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-dubbo</artifactId>\n            <version>5.10.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-consumer/src/main/java/cn/iocoder/springcloud/labx13/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx13.consumerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-consumer/src/main/java/cn/iocoder/springcloud/labx13/consumerdemo/controller/UserController.java",
    "content": "package cn.iocoder.springcloud.labx13.consumerdemo.controller;\n\nimport cn.iocoder.springcloud.labx13.api.UserService;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Reference(protocol = \"dubbo\", version = \"1.0.0\")\n    private UserService userService;\n\n    @GetMapping(\"/get\")\n    public String  get(@RequestParam(\"id\") Integer id) {\n        return userService.get(id);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n  # Zipkin 配置项，对应 ZipkinProperties 类\n  zipkin:\n    base-url: http://127.0.0.1:9411 # Zipkin 服务的地址\n\n# Dubbo 配置项，对应 DubboConfigurationProperties 类\ndubbo:\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Dubbo 服务提供者的配置，对应 ConsumerConfig 类\n  consumer:\n    filter: tracing\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: demo-provider # 设置订阅的应用列表，默认为 * 订阅所有应用。\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13-sc-sleuth-dubbo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-dubbo-provider</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n    -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-13-sc-sleuth-dubbo-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Sleuth + Zipkin 相关依赖，实现对它们的自动配置，从而实现链路追踪 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zipkin</artifactId>\n        </dependency>\n\n        <!-- Brave 针对 Dubbo 的插件，实现链路追踪 -->\n        <dependency>\n            <groupId>io.zipkin.brave</groupId>\n            <artifactId>brave-instrumentation-dubbo</artifactId>\n            <version>5.10.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-provider/src/main/java/cn/iocoder/springcloud/labx13/providerdemo/ProviderApplication.java",
    "content": "package cn.iocoder.springcloud.labx13.providerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProviderApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-provider/src/main/java/cn/iocoder/springcloud/labx13/providerdemo/service/UserServiceImpl.java",
    "content": "package cn.iocoder.springcloud.labx13.providerdemo.service;\n\nimport cn.iocoder.springcloud.labx13.api.UserService;\n\n@org.apache.dubbo.config.annotation.Service(protocol = \"dubbo\", version = \"1.0.0\")\npublic class UserServiceImpl implements UserService {\n\n    @Override\n    public String get(Integer id) {\n        return \"user:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-dubbo/labx-13-sc-sleuth-dubbo-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\n  # Zipkin 配置项，对应 ZipkinProperties 类\n  zipkin:\n    base-url: http://127.0.0.1:9411 # Zipkin 服务的地址\n\n# Dubbo 配置项，对应 DubboConfigurationProperties 类\ndubbo:\n  scan:\n    base-packages: cn.iocoder.springcloud.labx13.providerdemo.service # 指定 Dubbo 服务实现类的扫描基准包\n  # Dubbo 服务暴露的协议配置，对应 ProtocolConfig Map\n  protocols:\n    dubbo:\n      name: dubbo # 协议名称\n      port: -1 # 协议端口，-1 表示自增端口，从 20880 开始\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Dubbo 服务提供者的配置，对应 ProviderConfig 类\n  provider:\n    filter: tracing\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: '' # 设置订阅的应用列表，默认为 * 订阅所有应用。\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-dubbo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-dubbo</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-13-sc-sleuth-dubbo-api</module>\n        <module>labx-13-sc-sleuth-dubbo-provider</module>\n        <module>labx-13-sc-sleuth-dubbo-consumer</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-feign/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-feign</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Sleuth + Zipkin 相关依赖，实现对它们的自动配置，从而实现链路追踪 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zipkin</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud OpenFeign 相关依赖，使用 OpenFeign 提供声明式调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-feign/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/FeignApplication.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\n\n@SpringBootApplication\n@EnableFeignClients\npublic class FeignApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(FeignApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-feign/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/controller/FeignController.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo.controller;\n\nimport cn.iocoder.springcloud.labx13.springmvcdemo.feign.UserServiceFeignClient;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/feign\")\npublic class FeignController {\n\n    @Autowired\n    private UserServiceFeignClient userServiceFeignClient;\n\n    @GetMapping(\"/get\")\n    public String get(@RequestParam(\"id\") Integer id) {\n        return userServiceFeignClient.get(id);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-feign/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/feign/UserServiceFeignClient.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo.feign;\n\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(name = \"user-service\", url = \"http://127.0.0.1:8080\")\npublic interface UserServiceFeignClient {\n\n    @GetMapping(\"/user/get\")\n    String get(@RequestParam(\"id\") Integer id);\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-feign/src/main/resources/application.yml",
    "content": "server:\n  port: 8081\n\nspring:\n  application:\n    name: feign-service # 服务名\n\n  # Zipkin 配置项，对应 ZipkinProperties 类\n  zipkin:\n    base-url: http://127.0.0.1:9411 # Zipkin 服务的地址\n\n  # Spring Cloud Sleuth 配置项\n  sleuth:\n    # Spring Cloud Sleuth 针对 Web 组件的配置项，例如说 SpringMVC\n    web:\n      enabled: true # 是否开启，默认为 true\n    # Spring Cloud Sleuth 针对 Feign 组件的配置项，对应 SleuthFeignProperties 类\n    feign:\n      enabled: true # 是否开启，默认为 true\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-logback/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-logback</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Sleuth + Zipkin 相关依赖，实现对它们的自动配置，从而实现链路追踪 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zipkin</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-logback/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/UserServiceApplication.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-logback/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/controller/UserController.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/get\")\n    public String get(@RequestParam(\"id\") Integer id) {\n        logger.info(\"测试日志\");\n        return \"user:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-logback/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: user-service # 服务名\n\n  # Zipkin 配置项，对应 ZipkinProperties 类\n  zipkin:\n    base-url: http://127.0.0.1:9411 # Zipkin 服务的地址\n\n  # Spring Cloud Sleuth 配置项\n  sleuth:\n    # Spring Cloud Sleuth 针对 Web 组件的配置项，例如说 SpringMVC\n    web:\n      enabled: true # 是否开启，默认为 true\n    # Spring Cloud Sleuth 针对 Slf4j 组件的配置项\n    log:\n      slf4j:\n        enabled: true # 是否开启，默认为 true\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-activemq/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-mq-activemq</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Sleuth + Zipkin 相关依赖，实现对它们的自动配置，从而实现链路追踪 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zipkin</artifactId>\n        </dependency>\n\n        <!-- 实现对 ActiveMQ 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-activemq</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-activemq/src/main/java/cn/iocoder/springboot/labx13/activemqdemo/ActiveMQApplication.java",
    "content": "package cn.iocoder.springboot.labx13.activemqdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ActiveMQApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ActiveMQApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-activemq/src/main/java/cn/iocoder/springboot/labx13/activemqdemo/consumer/DemoConsumer.java",
    "content": "package cn.iocoder.springboot.labx13.activemqdemo.consumer;\n\nimport cn.iocoder.springboot.labx13.activemqdemo.message.DemoMessage;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.jms.annotation.JmsListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class DemoConsumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @JmsListener(destination = DemoMessage.QUEUE)\n    public void onMessage(DemoMessage message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-activemq/src/main/java/cn/iocoder/springboot/labx13/activemqdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springboot.labx13.activemqdemo.controller;\n\nimport cn.iocoder.springboot.labx13.activemqdemo.producer.DemoProducer;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private DemoProducer producer;\n\n    @GetMapping(\"/activemq\")\n    public String echo() {\n        this.sendMessage(1);\n        return \"activemq\";\n    }\n\n    public void sendMessage(Integer id) {\n        producer.syncSend(id);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-activemq/src/main/java/cn/iocoder/springboot/labx13/activemqdemo/message/DemoMessage.java",
    "content": "package cn.iocoder.springboot.labx13.activemqdemo.message;\n\nimport java.io.Serializable;\n\npublic class DemoMessage implements Serializable {\n\n    public static final String QUEUE = \"QUEUE_DEMO_\";\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public DemoMessage setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"DemoMessage{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-activemq/src/main/java/cn/iocoder/springboot/labx13/activemqdemo/producer/DemoProducer.java",
    "content": "package cn.iocoder.springboot.labx13.activemqdemo.producer;\n\nimport cn.iocoder.springboot.labx13.activemqdemo.message.DemoMessage;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.jms.core.JmsMessagingTemplate;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class DemoProducer {\n\n    @Autowired\n    private JmsMessagingTemplate jmsTemplate;\n\n    public void syncSend(Integer id) {\n        // 创建 DemoMessage 消息\n        DemoMessage message = new DemoMessage();\n        message.setId(id);\n        // 同步发送消息\n        jmsTemplate.convertAndSend(DemoMessage.QUEUE, message);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-activemq/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-application-activemq\n\n  # ActiveMQ 配置项，对应 ActiveMQProperties 配置类\n  activemq:\n    broker-url: tcp://127.0.0.1:61616 # Activemq Broker 的地址\n    user: admin # 账号\n    password: admin # 密码\n    packages:\n      trust-all: true # 可信任的反序列化包\n\n  # Zipkin 配置项，对应 ZipkinProperties 类\n  zipkin:\n    base-url: http://127.0.0.1:9411 # Zipkin 服务的地址\n\n  # Spring Cloud Sleuth 配置项\n  sleuth:\n    messaging:\n      # Spring Cloud Sleuth 针对 JMS 组件的配置项\n      jms:\n        enabled: true # 是否开启\n        remote-service-name: jms # 远程服务名，默认为 jms\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-kafka/labx-13-sc-sleuth-mq-kafka-producer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13-sc-sleuth-mq-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-mq-kafka-producer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream kafka 相关依赖，将 kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Sleuth + Zipkin 相关依赖，实现对它们的自动配置，从而实现链路追踪 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zipkin</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-kafka/labx-13-sc-sleuth-mq-kafka-producer/src/main/java/cn/iocoder/springcloud/labx13/kafkademo/producerdemo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloud.labx13.kafkademo.producerdemo;\n\nimport cn.iocoder.springcloud.labx13.kafkademo.producerdemo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-kafka/labx-13-sc-sleuth-mq-kafka-producer/src/main/java/cn/iocoder/springcloud/labx13/kafkademo/producerdemo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloud.labx13.kafkademo.producerdemo.controller;\n\nimport cn.iocoder.springcloud.labx13.kafkademo.producerdemo.message.Demo01Message;\nimport cn.iocoder.springcloud.labx13.kafkademo.producerdemo.message.MySource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send\")\n    public boolean send() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .build();\n        // 发送消息\n        return mySource.demo01Output().send(springMessage);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-kafka/labx-13-sc-sleuth-mq-kafka-producer/src/main/java/cn/iocoder/springcloud/labx13/kafkademo/producerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx13.kafkademo.producerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-kafka/labx-13-sc-sleuth-mq-kafka-producer/src/main/java/cn/iocoder/springcloud/labx13/kafkademo/producerdemo/message/MySource.java",
    "content": "package cn.iocoder.springcloud.labx13.kafkademo.producerdemo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-kafka/labx-13-sc-sleuth-mq-kafka-producer/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      #      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n        # Kafka 自定义 Binding 配置项，对应 KafkaBindingProperties Map\n        bindings:\n          demo01-output:\n            # Kafka Producer 配置项，对应 KafkaProducerProperties 类\n            producer:\n              sync: true # 是否同步发送消息，默认为 false 异步。\n\n  # Zipkin 配置项，对应 ZipkinProperties 类\n  zipkin:\n    base-url: http://127.0.0.1:9411 # Zipkin 服务的地址\n\n  # Spring Cloud Sleuth 配置项\n  sleuth:\n    messaging:\n      # Spring Cloud Sleuth 针对 Kafka 组件的配置项\n      kafka:\n        enabled: true # 是否开启\n        remote-service-name: kafka # 远程服务名，默认为 kafka\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-kafka/labx-13-sc-stream-mq-kafka-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13-sc-sleuth-mq-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-stream-mq-kafka-consumer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Sleuth + Zipkin 相关依赖，实现对它们的自动配置，从而实现链路追踪 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zipkin</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-kafka/labx-13-sc-stream-mq-kafka-consumer/src/main/java/cn/iocoder/springcloud/labx13/kafkademo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx13.kafkademo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx13.kafkademo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-kafka/labx-13-sc-stream-mq-kafka-consumer/src/main/java/cn/iocoder/springcloud/labx13/kafkademo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx13.kafkademo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx13.kafkademo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-kafka/labx-13-sc-stream-mq-kafka-consumer/src/main/java/cn/iocoder/springcloud/labx13/kafkademo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx13.kafkademo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-kafka/labx-13-sc-stream-mq-kafka-consumer/src/main/java/cn/iocoder/springcloud/labx13/kafkademo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx13.kafkademo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-kafka/labx-13-sc-stream-mq-kafka-consumer/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      #      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group # 消费者分组\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n\n  # Zipkin 配置项，对应 ZipkinProperties 类\n  zipkin:\n    base-url: http://127.0.0.1:9411 # Zipkin 服务的地址\n\n  # Spring Cloud Sleuth 配置项\n  sleuth:\n    messaging:\n      # Spring Cloud Sleuth 针对 kafka 组件的配置项kafka\n      kafka:\n        enabled: true # 是否开启\n        remote-service-name: kafka # 远程服务名，默认为 kafka\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-kafka/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-mq-kafka</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-13-sc-sleuth-mq-kafka-producer</module>\n        <module>labx-13-sc-stream-mq-kafka-consumer</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-rabbitmq/labx-13-sc-sleuth-mq-rabbitmq-producer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13-sc-sleuth-mq-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-mq-rabbitmq-producer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Sleuth + Zipkin 相关依赖，实现对它们的自动配置，从而实现链路追踪 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zipkin</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-rabbitmq/labx-13-sc-sleuth-mq-rabbitmq-producer/src/main/java/cn/iocoder/springcloud/labx13/rabbitmqdemo/producerdemo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloud.labx13.rabbitmqdemo.producerdemo;\n\nimport cn.iocoder.springcloud.labx13.rabbitmqdemo.producerdemo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-rabbitmq/labx-13-sc-sleuth-mq-rabbitmq-producer/src/main/java/cn/iocoder/springcloud/labx13/rabbitmqdemo/producerdemo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloud.labx13.rabbitmqdemo.producerdemo.controller;\n\nimport cn.iocoder.springcloud.labx13.rabbitmqdemo.producerdemo.message.Demo01Message;\nimport cn.iocoder.springcloud.labx13.rabbitmqdemo.producerdemo.message.MySource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send\")\n    public boolean send() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .build();\n        // 发送消息\n        return mySource.demo01Output().send(springMessage);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-rabbitmq/labx-13-sc-sleuth-mq-rabbitmq-producer/src/main/java/cn/iocoder/springcloud/labx13/rabbitmqdemo/producerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx13.rabbitmqdemo.producerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-rabbitmq/labx-13-sc-sleuth-mq-rabbitmq-producer/src/main/java/cn/iocoder/springcloud/labx13/rabbitmqdemo/producerdemo/message/MySource.java",
    "content": "package cn.iocoder.springcloud.labx13.rabbitmqdemo.producerdemo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-rabbitmq/labx-13-sc-sleuth-mq-rabbitmq-producer/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          binder: rabbit001 # 设置使用的 Binder 名字\n\n  # Zipkin 配置项，对应 ZipkinProperties 类\n  zipkin:\n    base-url: http://127.0.0.1:9411 # Zipkin 服务的地址\n\n  # Spring Cloud Sleuth 配置项\n  sleuth:\n    messaging:\n      # Spring Cloud Sleuth 针对 RabbitMQ 组件的配置项\n      rabbit:\n        enabled: true # 是否开启\n        remote-service-name: rabbitmq # 远程服务名，默认为 rabbitmq\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-rabbitmq/labx-13-sc-stream-mq-rabbitmq-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13-sc-sleuth-mq-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-stream-mq-rabbitmq-consumer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Sleuth + Zipkin 相关依赖，实现对它们的自动配置，从而实现链路追踪 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zipkin</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-rabbitmq/labx-13-sc-stream-mq-rabbitmq-consumer/src/main/java/cn/iocoder/springcloud/labx13/rabbitmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx13.rabbitmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx13.rabbitmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-rabbitmq/labx-13-sc-stream-mq-rabbitmq-consumer/src/main/java/cn/iocoder/springcloud/labx13/rabbitmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx13.rabbitmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx13.rabbitmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-rabbitmq/labx-13-sc-stream-mq-rabbitmq-consumer/src/main/java/cn/iocoder/springcloud/labx13/rabbitmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx13.rabbitmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-rabbitmq/labx-13-sc-stream-mq-rabbitmq-consumer/src/main/java/cn/iocoder/springcloud/labx13/rabbitmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx13.rabbitmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-rabbitmq/labx-13-sc-stream-mq-rabbitmq-consumer/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n          binder: rabbit001  # 设置使用的 Binder 名字\n\n  # Zipkin 配置项，对应 ZipkinProperties 类\n  zipkin:\n    base-url: http://127.0.0.1:9411 # Zipkin 服务的地址\n\n  # Spring Cloud Sleuth 配置项\n  sleuth:\n    messaging:\n      # Spring Cloud Sleuth 针对 RabbitMQ 组件的配置项\n      rabbit:\n        enabled: true # 是否开启\n        remote-service-name: rabbitmq # 远程服务名，默认为 rabbitmq\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-mq-rabbitmq/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-mq-rabbitmq</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-13-sc-sleuth-mq-rabbitmq-producer</module>\n        <module>labx-13-sc-stream-mq-rabbitmq-consumer</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-opentracing/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-opentracing</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Sleuth + Zipkin 相关依赖，实现对它们的自动配置，从而实现链路追踪 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zipkin</artifactId>\n        </dependency>\n\n        <!--  Brave 对 Opentracing 的实现 -->\n        <dependency>\n            <groupId>io.opentracing.brave</groupId>\n            <artifactId>brave-opentracing</artifactId>\n            <version>0.35.0</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-opentracing/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/UserServiceApplication.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-opentracing/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/controller/UserController.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo.controller;\n\nimport io.opentracing.Tracer;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Autowired\n    private Tracer tracer;\n\n    @GetMapping(\"/get\")\n    public String get(@RequestParam(\"id\") Integer id) {\n        // 创建一个 Span\n        tracer.buildSpan(\"custom_operation\").withTag(\"mp\", \"芋道源码\").start().finish();\n\n        return \"user:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-opentracing/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: user-service # 服务名\n\n  # Zipkin 配置项，对应 ZipkinProperties 类\n  zipkin:\n    base-url: http://127.0.0.1:9411 # Zipkin 服务的地址\n\n  # Spring Cloud Sleuth 配置项\n  sleuth:\n    # Spring Cloud Sleuth 针对 Web 组件的配置项，例如说 SpringMVC\n    web:\n      enabled: true # 是否开启，默认为 true\n    # Spring Cloud Sleuth 针对 OpenTracing 组件的配置项\n    opentracing:\n      enabled: true # 是否开启，默认为 true\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-sampler/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-sampler</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Sleuth + Zipkin 相关依赖，实现对它们的自动配置，从而实现链路追踪 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zipkin</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-sampler/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/UserServiceApplication.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-sampler/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/controller/UserController.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @GetMapping(\"/get\")\n    public String get(@RequestParam(\"id\") Integer id) {\n        return \"user:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-sampler/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: user-service # 服务名\n\n  # Zipkin 配置项，对应 ZipkinProperties 类\n  zipkin:\n    base-url: http://127.0.0.1:9411 # Zipkin 服务的地址\n\n  # Spring Cloud Sleuth 配置项\n  sleuth:\n    # Spring Cloud Sleuth 针对 Web 组件的配置项，例如说 SpringMVC\n    web:\n      enabled: true # 是否开启，默认为 true\n    # Spring Cloud Sleuth 针对抽样收集的配置项\n    sampler:\n      probability: 0.1 # 采样百分比，默认为空。\n#      rate: 1 # 限流采样，即每秒可收集链路的数量，默认为 10。\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-springcloudgateway/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-springcloudgateway</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Gateway 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-gateway</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Sleuth + Zipkin 相关依赖，实现对它们的自动配置，从而实现链路追踪 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zipkin</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-springcloudgateway/src/main/java/cn/iocoder/springcloud/labx13/gatewaydemo/GatewayApplication.java",
    "content": "package cn.iocoder.springcloud.labx13.gatewaydemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class GatewayApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(GatewayApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-springcloudgateway/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: gateway-application\n\n  # Zipkin 配置项，对应 ZipkinProperties 类\n  zipkin:\n    base-url: http://127.0.0.1:9411 # Zipkin 服务的地址\n\n  # Spring Cloud Sleuth 配置项\n  sleuth:\n    # Spring Cloud Sleuth 针对 Web 组件的配置项，例如说 SpringMVC\n    web:\n      enabled: true # 是否开启，默认为 true\n\n  cloud:\n    # Spring Cloud Gateway 配置项，对应 GatewayProperties 类\n    gateway:\n      # 路由配置项，对应 RouteDefinition 数组\n      routes:\n        - id: feign-service-route\n          uri: http://127.0.0.1:8081\n          predicates:\n            - Path=/**\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-springmvc/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-springmvc</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Sleuth + Zipkin 相关依赖，实现对它们的自动配置，从而实现链路追踪 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zipkin</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-springmvc/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/UserServiceApplication.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-springmvc/src/main/java/cn/iocoder/springcloud/labx13/springmvcdemo/controller/UserController.java",
    "content": "package cn.iocoder.springcloud.labx13.springmvcdemo.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @GetMapping(\"/get\")\n    public String get(@RequestParam(\"id\") Integer id) {\n        return \"user:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-springmvc/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: user-service # 服务名\n\n  # Zipkin 配置项，对应 ZipkinProperties 类\n  zipkin:\n    base-url: http://127.0.0.1:9411 # Zipkin 服务的地址\n\n  # Spring Cloud Sleuth 配置项\n  sleuth:\n    # Spring Cloud Sleuth 针对 Web 组件的配置项，例如说 SpringMVC\n    web:\n      enabled: true # 是否开启，默认为 true\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-zipkin-server-demo-in-memory/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-13</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13-sc-sleuth-zipkin-server-demo-in-memory</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <sleuth.version>2.2.2.RELEASE</sleuth.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n<!--            <dependency>-->\n<!--                <groupId>org.springframework.cloud</groupId>-->\n<!--                <artifactId>spring-cloud-starter-sleuth</artifactId>-->\n<!--                <version>${sleuth.version}</version>-->\n<!--                <type>pom</type>-->\n<!--                <scope>import</scope>-->\n<!--            </dependency>-->\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- Zipkin 服务器 -->\n        <dependency>\n            <groupId>io.zipkin.java</groupId>\n            <artifactId>zipkin-server</artifactId>\n            <version>2.12.9</version>\n        </dependency>\n        <!-- Zipkin UI -->\n        <dependency>\n            <groupId>io.zipkin.java</groupId>\n            <artifactId>zipkin-autoconfigure-ui</artifactId>\n            <version>2.12.9</version>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-sleuth</artifactId>\n            <version>2.2.2.RELEASE</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-zipkin-server-demo-in-memory/src/main/java/cn/iocoder/springcloud/labx13/sleuthzipkinserverdemo/SleuthZipkinServerApplication.java",
    "content": "package cn.iocoder.springcloud.labx13.sleuthzipkinserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport zipkin2.server.internal.EnableZipkinServer;\n\n@SpringBootApplication\n@EnableZipkinServer\npublic class SleuthZipkinServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(SleuthZipkinServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-13/labx-13-sc-sleuth-zipkin-server-demo-in-memory/src/main/resources/application.yml",
    "content": "server:\n  port: 9411\n\n\n"
  },
  {
    "path": "labx-13/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-13</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-13-sc-sleuth-zipkin-server-demo-in-memory</module>\n\n        <module>labx-13-sc-sleuth-springmvc</module>\n        <module>labx-13-sc-sleuth-feign</module>\n        <module>labx-13-sc-sleuth-springcloudgateway</module>\n        <module>labx-13-sc-sleuth-dubbo</module>\n\n        <module>labx-13-sc-sleuth-db-mysql</module>\n        <module>labx-13-sc-sleuth-db-redis</module>\n        <module>labx-13-sc-sleuth-db-mongodb</module>\n        <module>labx-13-sc-sleuth-db-elasticsearch</module>\n\n        <module>labx-13-sc-sleuth-mq-rabbitmq</module>\n        <module>labx-13-sc-sleuth-mq-kafka</module>\n        <module>labx-13-sc-sleuth-mq-activemq</module>\n\n        <module>labx-13-sc-sleuth-logback</module>\n        <module>labx-13-sc-sleuth-opentracing</module>\n        <module>labx-13-sc-sleuth-sampler</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-13/《芋道 Spring Cloud 链路追踪 Spring Cloud Sleuth》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/Spring-Cloud-Sleuth/?github>\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-14-sc-skywalking-dubbo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-14-sc-skywalking-dubbo-api</artifactId>\n\n\n</project>\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-api/src/main/java/cn/iocoder/springcloud/labx14/api/UserService.java",
    "content": "package cn.iocoder.springcloud.labx14.api;\n\n/**\n * 用户服务 RPC Service 接口\n */\npublic interface UserService {\n\n    /**\n     * 根据指定用户编号，获得用户信息\n     *\n     * @param id 用户编号\n     * @return 用户信息\n     */\n    String get(Integer id);\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-api/src/main/java/cn/iocoder/springcloud/labx14/package-info.java",
    "content": "package cn.iocoder.springcloud.labx13;\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-14-sc-skywalking-dubbo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-14-sc-skywalking-dubbo-consumer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n    -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-14-sc-skywalking-dubbo-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-consumer/src/main/java/cn/iocoder/springcloud/labx14/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx14.consumerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-consumer/src/main/java/cn/iocoder/springcloud/labx14/consumerdemo/controller/UserController.java",
    "content": "package cn.iocoder.springcloud.labx14.consumerdemo.controller;\n\nimport cn.iocoder.springcloud.labx14.api.UserService;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @Reference(protocol = \"dubbo\", version = \"1.0.0\")\n    private UserService userService;\n\n    @GetMapping(\"/get\")\n    public String  get(@RequestParam(\"id\") Integer id) {\n        return userService.get(id);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-consumer/src/main/resources/application.yaml",
    "content": "server:\n  port: 8079\n\nspring:\n  application:\n    name: demo-consumer\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n# Dubbo 配置项，对应 DubboConfigurationProperties 类\ndubbo:\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: demo-provider # 设置订阅的应用列表，默认为 * 订阅所有应用。\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-14-sc-skywalking-dubbo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-14-sc-skywalking-dubbo-provider</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n    -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入定义的 Dubbo API 接口 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-14-sc-skywalking-dubbo-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-provider/src/main/java/cn/iocoder/springcloud/labx14/providerdemo/ProviderApplication.java",
    "content": "package cn.iocoder.springcloud.labx14.providerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProviderApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-provider/src/main/java/cn/iocoder/springcloud/labx14/providerdemo/service/UserServiceImpl.java",
    "content": "package cn.iocoder.springcloud.labx14.providerdemo.service;\n\n\nimport cn.iocoder.springcloud.labx14.api.UserService;\n\n@org.apache.dubbo.config.annotation.Service(protocol = \"dubbo\", version = \"1.0.0\")\npublic class UserServiceImpl implements UserService {\n\n    @Override\n    public String get(Integer id) {\n        return \"user:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-dubbo/labx-14-sc-skywalking-dubbo-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider\n\n# Dubbo 配置项，对应 DubboConfigurationProperties 类\ndubbo:\n  scan:\n    base-packages: cn.iocoder.springcloud.labx14.providerdemo.service # 指定 Dubbo 服务实现类的扫描基准包\n  # Dubbo 服务暴露的协议配置，对应 ProtocolConfig Map\n  protocols:\n    dubbo:\n      name: dubbo # 协议名称\n      port: -1 # 协议端口，-1 表示自增端口，从 20880 开始\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: '' # 设置订阅的应用列表，默认为 * 订阅所有应用。\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-dubbo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-14</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-14-sc-skywalking-dubbo</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-14-sc-skywalking-dubbo-api</module>\n        <module>labx-14-sc-skywalking-dubbo-provider</module>\n        <module>labx-14-sc-skywalking-dubbo-consumer</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-feign/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-14</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-14-sc-skywalking-feign</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud OpenFeign 相关依赖，使用 OpenFeign 提供声明式调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-feign/src/main/java/cn/iocoder/springcloud/labx14/springmvcdemo/FeignApplication.java",
    "content": "package cn.iocoder.springcloud.labx14.springmvcdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\n\n@SpringBootApplication\n@EnableFeignClients\npublic class FeignApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(FeignApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-feign/src/main/java/cn/iocoder/springcloud/labx14/springmvcdemo/controller/FeignController.java",
    "content": "package cn.iocoder.springcloud.labx14.springmvcdemo.controller;\n\nimport cn.iocoder.springcloud.labx14.springmvcdemo.feign.UserServiceFeignClient;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/feign\")\npublic class FeignController {\n\n    @Autowired\n    private UserServiceFeignClient userServiceFeignClient;\n\n    @GetMapping(\"/get\")\n    public String get(@RequestParam(\"id\") Integer id) {\n        return userServiceFeignClient.get(id);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-feign/src/main/java/cn/iocoder/springcloud/labx14/springmvcdemo/feign/UserServiceFeignClient.java",
    "content": "package cn.iocoder.springcloud.labx14.springmvcdemo.feign;\n\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(name = \"user-service\", url = \"http://127.0.0.1:8079\")\npublic interface UserServiceFeignClient {\n\n    @GetMapping(\"/user/get\")\n    String get(@RequestParam(\"id\") Integer id);\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-feign/src/main/resources/application.yml",
    "content": "server:\n  port: 8081\n\nspring:\n  application:\n    name: feign-service # 服务名\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-kafka/labx-14-sc-skywalking-mq-kafka-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-14-sc-skywalking-mq-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-14-sc-skywalking-mq-kafka-consumer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-kafka/labx-14-sc-skywalking-mq-kafka-consumer/src/main/java/cn/iocoder/springcloud/labx14/kafkademo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx14.kafkademo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx14.kafkademo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-kafka/labx-14-sc-skywalking-mq-kafka-consumer/src/main/java/cn/iocoder/springcloud/labx14/kafkademo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx14.kafkademo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx14.kafkademo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-kafka/labx-14-sc-skywalking-mq-kafka-consumer/src/main/java/cn/iocoder/springcloud/labx14/kafkademo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx14.kafkademo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-kafka/labx-14-sc-skywalking-mq-kafka-consumer/src/main/java/cn/iocoder/springcloud/labx14/kafkademo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx14.kafkademo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-kafka/labx-14-sc-skywalking-mq-kafka-consumer/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      #      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group # 消费者分组\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-kafka/labx-14-sc-skywalking-mq-kafka-producer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-14-sc-skywalking-mq-kafka</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-14-sc-skywalking-mq-kafka-producer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-kafka/labx-14-sc-skywalking-mq-kafka-producer/src/main/java/cn/iocoder/springcloud/labx14/kafkademo/producerdemo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloud.labx14.kafkademo.producerdemo;\n\nimport cn.iocoder.springcloud.labx14.kafkademo.producerdemo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-kafka/labx-14-sc-skywalking-mq-kafka-producer/src/main/java/cn/iocoder/springcloud/labx14/kafkademo/producerdemo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloud.labx14.kafkademo.producerdemo.controller;\n\nimport cn.iocoder.springcloud.labx14.kafkademo.producerdemo.message.Demo01Message;\nimport cn.iocoder.springcloud.labx14.kafkademo.producerdemo.message.MySource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send\")\n    public boolean send() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .build();\n        // 发送消息\n        return mySource.demo01Output().send(springMessage);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-kafka/labx-14-sc-skywalking-mq-kafka-producer/src/main/java/cn/iocoder/springcloud/labx14/kafkademo/producerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx14.kafkademo.producerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-kafka/labx-14-sc-skywalking-mq-kafka-producer/src/main/java/cn/iocoder/springcloud/labx14/kafkademo/producerdemo/message/MySource.java",
    "content": "package cn.iocoder.springcloud.labx14.kafkademo.producerdemo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-kafka/labx-14-sc-skywalking-mq-kafka-producer/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      #      binders:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 Kafka Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n      # Spring Cloud Stream Kafka 配置项\n      kafka:\n        # Kafka Binder 配置项，对应 KafkaBinderConfigurationProperties 类\n        binder:\n          brokers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n        # Kafka 自定义 Binding 配置项，对应 KafkaBindingProperties Map\n        bindings:\n          demo01-output:\n            # Kafka Producer 配置项，对应 KafkaProducerProperties 类\n            producer:\n              sync: true # 是否同步发送消息，默认为 false 异步。\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-kafka/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-14</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-14-sc-skywalking-mq-kafka</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-14-sc-skywalking-mq-kafka-producer</module>\n        <module>labx-14-sc-skywalking-mq-kafka-consumer</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rabbitmq/labx-14-sc-skywalking-mq-rabbitmq-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-14-sc-skywalking-mq-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-14-sc-skywalking-mq-rabbitmq-consumer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rabbitmq/labx-14-sc-skywalking-mq-rabbitmq-consumer/src/main/java/cn/iocoder/springcloud/labx14/rabbitmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx14.rabbitmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx14.rabbitmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rabbitmq/labx-14-sc-skywalking-mq-rabbitmq-consumer/src/main/java/cn/iocoder/springcloud/labx14/rabbitmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx14.rabbitmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx14.rabbitmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rabbitmq/labx-14-sc-skywalking-mq-rabbitmq-consumer/src/main/java/cn/iocoder/springcloud/labx14/rabbitmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx14.rabbitmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rabbitmq/labx-14-sc-skywalking-mq-rabbitmq-consumer/src/main/java/cn/iocoder/springcloud/labx14/rabbitmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx14.rabbitmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rabbitmq/labx-14-sc-skywalking-mq-rabbitmq-consumer/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n          binder: rabbit001  # 设置使用的 Binder 名字\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rabbitmq/labx-14-sc-skywalking-mq-rabbitmq-producer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-14-sc-skywalking-mq-rabbitmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-14-sc-skywalking-mq-rabbitmq-producer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream RabbitMQ 相关依赖，将 RabbitMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rabbitmq/labx-14-sc-skywalking-mq-rabbitmq-producer/src/main/java/cn/iocoder/springcloud/labx14/rabbitmqdemo/producerdemo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloud.labx14.rabbitmqdemo.producerdemo;\n\nimport cn.iocoder.springcloud.labx14.rabbitmqdemo.producerdemo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rabbitmq/labx-14-sc-skywalking-mq-rabbitmq-producer/src/main/java/cn/iocoder/springcloud/labx14/rabbitmqdemo/producerdemo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloud.labx14.rabbitmqdemo.producerdemo.controller;\n\nimport cn.iocoder.springcloud.labx14.rabbitmqdemo.producerdemo.message.Demo01Message;\nimport cn.iocoder.springcloud.labx14.rabbitmqdemo.producerdemo.message.MySource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send\")\n    public boolean send() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .build();\n        // 发送消息\n        return mySource.demo01Output().send(springMessage);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rabbitmq/labx-14-sc-skywalking-mq-rabbitmq-producer/src/main/java/cn/iocoder/springcloud/labx14/rabbitmqdemo/producerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx14.rabbitmqdemo.producerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rabbitmq/labx-14-sc-skywalking-mq-rabbitmq-producer/src/main/java/cn/iocoder/springcloud/labx14/rabbitmqdemo/producerdemo/message/MySource.java",
    "content": "package cn.iocoder.springcloud.labx14.rabbitmqdemo.producerdemo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rabbitmq/labx-14-sc-skywalking-mq-rabbitmq-producer/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binder 配置项，对应 BinderProperties Map\n      binders:\n        rabbit001:\n          type: rabbit # 设置 Binder 的类型\n          environment: # 设置 Binder 的环境配置\n            # 如果是 RabbitMQ 类型的时候，则对应的是 RabbitProperties 类\n            spring:\n              rabbitmq:\n                host: 127.0.0.1 # RabbitMQ 服务的地址\n                port: 5672 # RabbitMQ 服务的端口\n                username: guest # RabbitMQ 服务的账号\n                password: guest # RabbitMQ 服务的密码\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RabbitMQ Exchange\n          content-type: application/json # 内容格式。这里使用 JSON\n          binder: rabbit001 # 设置使用的 Binder 名字\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rabbitmq/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-14</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-14-sc-skywalking-mq-rabbitmq</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-14-sc-skywalking-mq-rabbitmq-producer</module>\n        <module>labx-14-sc-skywalking-mq-rabbitmq-consumer</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rocketmq/labx-14-sc-skywalking-mq-rocketmq-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-14-sc-skywalking-mq-rocketmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-14-sc-skywalking-mq-rocketmq-consumer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Stream RocketMQ 相关依赖，将 RocketMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rocketmq/labx-14-sc-skywalking-mq-rocketmq-consumer/src/main/java/cn/iocoder/springcloud/labx14/rocketmqdemo/consumerdemo/ConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx14.rocketmqdemo.consumerdemo;\n\nimport cn.iocoder.springcloud.labx14.rocketmqdemo.consumerdemo.listener.MySink;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySink.class)\npublic class ConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConsumerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rocketmq/labx-14-sc-skywalking-mq-rocketmq-consumer/src/main/java/cn/iocoder/springcloud/labx14/rocketmqdemo/consumerdemo/listener/Demo01Consumer.java",
    "content": "package cn.iocoder.springcloud.labx14.rocketmqdemo.consumerdemo.listener;\n\nimport cn.iocoder.springcloud.labx14.rocketmqdemo.consumerdemo.message.Demo01Message;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.cloud.stream.annotation.StreamListener;\nimport org.springframework.messaging.handler.annotation.Payload;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class Demo01Consumer {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @StreamListener(MySink.DEMO01_INPUT)\n    public void onMessage(@Payload Demo01Message message) {\n        logger.info(\"[onMessage][线程编号:{} 消息内容：{}]\", Thread.currentThread().getId(), message);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rocketmq/labx-14-sc-skywalking-mq-rocketmq-consumer/src/main/java/cn/iocoder/springcloud/labx14/rocketmqdemo/consumerdemo/listener/MySink.java",
    "content": "package cn.iocoder.springcloud.labx14.rocketmqdemo.consumerdemo.listener;\n\nimport org.springframework.cloud.stream.annotation.Input;\nimport org.springframework.messaging.SubscribableChannel;\n\npublic interface MySink {\n\n    String DEMO01_INPUT = \"demo01-input\";\n\n    @Input(DEMO01_INPUT)\n    SubscribableChannel demo01Input();\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rocketmq/labx-14-sc-skywalking-mq-rocketmq-consumer/src/main/java/cn/iocoder/springcloud/labx14/rocketmqdemo/consumerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx14.rocketmqdemo.consumerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rocketmq/labx-14-sc-skywalking-mq-rocketmq-consumer/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-consumer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-input:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n          group: demo01-consumer-group-DEMO-TOPIC-01 # 消费者分组\n      # Spring Cloud Stream RocketMQ 配置项\n      rocketmq:\n        # RocketMQ Binder 配置项，对应 RocketMQBinderConfigurationProperties 类\n        binder:\n          name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址\n        # RocketMQ 自定义 Binding 配置项，对应 RocketMQBindingProperties Map\n        bindings:\n          demo01-input:\n            # RocketMQ Consumer 配置项，对应 RocketMQConsumerProperties 类\n            consumer:\n              enabled: true # 是否开启消费，默认为 true\n              broadcasting: false # 是否使用广播消费，默认为 false 使用集群消费\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rocketmq/labx-14-sc-skywalking-mq-rocketmq-producer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-14-sc-skywalking-mq-rocketmq</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-14-sc-skywalking-mq-rocketmq-producer</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Stream RocketMQ 相关依赖，将 RocketMQ 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rocketmq/labx-14-sc-skywalking-mq-rocketmq-producer/src/main/java/cn/iocoder/springcloud/labx14/rocketmqdemo/producerdemo/ProducerApplication.java",
    "content": "package cn.iocoder.springcloud.labx14.rocketmqdemo.producerdemo;\n\nimport cn.iocoder.springcloud.labx14.rocketmqdemo.producerdemo.message.MySource;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.stream.annotation.EnableBinding;\n\n@SpringBootApplication\n@EnableBinding(MySource.class)\npublic class ProducerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProducerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rocketmq/labx-14-sc-skywalking-mq-rocketmq-producer/src/main/java/cn/iocoder/springcloud/labx14/rocketmqdemo/producerdemo/controller/Demo01Controller.java",
    "content": "package cn.iocoder.springcloud.labx14.rocketmqdemo.producerdemo.controller;\n\nimport cn.iocoder.springcloud.labx14.rocketmqdemo.producerdemo.message.Demo01Message;\nimport cn.iocoder.springcloud.labx14.rocketmqdemo.producerdemo.message.MySource;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.messaging.Message;\nimport org.springframework.messaging.support.MessageBuilder;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.Random;\n\n@RestController\n@RequestMapping(\"/demo01\")\npublic class Demo01Controller {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private MySource mySource;\n\n    @GetMapping(\"/send\")\n    public boolean send() {\n        // 创建 Message\n        Demo01Message message = new Demo01Message()\n                .setId(new Random().nextInt());\n        // 创建 Spring Message 对象\n        Message<Demo01Message> springMessage = MessageBuilder.withPayload(message)\n                .build();\n        // 发送消息\n        return mySource.demo01Output().send(springMessage);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rocketmq/labx-14-sc-skywalking-mq-rocketmq-producer/src/main/java/cn/iocoder/springcloud/labx14/rocketmqdemo/producerdemo/message/Demo01Message.java",
    "content": "package cn.iocoder.springcloud.labx14.rocketmqdemo.producerdemo.message;\n\n/**\n * 示例 01 的 Message 消息\n */\npublic class Demo01Message {\n\n    /**\n     * 编号\n     */\n    private Integer id;\n\n    public Demo01Message setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Integer getId() {\n        return id;\n    }\n\n    @Override\n    public String toString() {\n        return \"Demo01Message{\" +\n                \"id=\" + id +\n                '}';\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rocketmq/labx-14-sc-skywalking-mq-rocketmq-producer/src/main/java/cn/iocoder/springcloud/labx14/rocketmqdemo/producerdemo/message/MySource.java",
    "content": "package cn.iocoder.springcloud.labx14.rocketmqdemo.producerdemo.message;\n\nimport org.springframework.cloud.stream.annotation.Output;\nimport org.springframework.messaging.MessageChannel;\n\npublic interface MySource {\n\n    @Output(\"demo01-output\")\n    MessageChannel demo01Output();\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rocketmq/labx-14-sc-skywalking-mq-rocketmq-producer/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-producer-application\n  cloud:\n    # Spring Cloud Stream 配置项，对应 BindingServiceProperties 类\n    stream:\n      # Binding 配置项，对应 BindingProperties Map\n      bindings:\n        demo01-output:\n          destination: DEMO-TOPIC-01 # 目的地。这里使用 RocketMQ Topic\n          content-type: application/json # 内容格式。这里使用 JSON\n      # Spring Cloud Stream RocketMQ 配置项\n      rocketmq:\n        # RocketMQ Binder 配置项，对应 RocketMQBinderConfigurationProperties 类\n        binder:\n          name-server: 127.0.0.1:9876 # RocketMQ Namesrv 地址\n        # RocketMQ 自定义 Binding 配置项，对应 RocketMQBindingProperties Map\n        bindings:\n          demo01-output:\n            # RocketMQ Producer 配置项，对应 RocketMQProducerProperties 类\n            producer:\n              group: test # 生产者分组\n              sync: true # 是否同步发送消息，默认为 false 异步。\n\nserver:\n  port: 18080\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-mq-rocketmq/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-14</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-14-sc-skywalking-mq-rocketmq</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-14-sc-skywalking-mq-rocketmq-producer</module>\n        <module>labx-14-sc-skywalking-mq-rocketmq-consumer</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-springcloudgateway/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-14</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-14-sc-skywalking-springcloudgateway</artifactId>\n\n    <properties>\n        <spring.boot.version>2.1.13.RELEASE</spring.boot.version>\n        <spring.cloud.version>Greenwich.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Gateway 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-gateway</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-springcloudgateway/src/main/java/cn/iocoder/springcloud/labx14/gatewaydemo/GatewayApplication.java",
    "content": "package cn.iocoder.springcloud.labx14.gatewaydemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class GatewayApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(GatewayApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-springcloudgateway/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: gateway-application\n\n  cloud:\n    # Spring Cloud Gateway 配置项，对应 GatewayProperties 类\n    gateway:\n      # 路由配置项，对应 RouteDefinition 数组\n      routes:\n        - id: feign-service-route\n          uri: http://127.0.0.1:8081\n          predicates:\n            - Path=/**\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-springmvc/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-14</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-14-sc-skywalking-springmvc</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-springmvc/src/main/java/cn/iocoder/springcloud/labx14/springmvcdemo/UserServiceApplication.java",
    "content": "package cn.iocoder.springcloud.labx14.springmvcdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-springmvc/src/main/java/cn/iocoder/springcloud/labx14/springmvcdemo/controller/UserController.java",
    "content": "package cn.iocoder.springcloud.labx14.springmvcdemo.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @GetMapping(\"/get\")\n    public String get(@RequestParam(\"id\") Integer id) {\n        return \"user:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-14/labx-14-sc-skywalking-springmvc/src/main/resources/application.yml",
    "content": "server:\n  port: 8079\n\nspring:\n  application:\n    name: user-service # 服务名\n"
  },
  {
    "path": "labx-14/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-14</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-14-sc-skywalking-springmvc</module>\n        <module>labx-14-sc-skywalking-feign</module>\n        <module>labx-14-sc-skywalking-springcloudgateway</module>\n        <module>labx-14-sc-skywalking-dubbo</module>\n\n        <module>labx-14-sc-skywalking-mq-rocketmq</module>\n        <module>labx-14-sc-skywalking-mq-rabbitmq</module>\n        <module>labx-14-sc-skywalking-mq-kafka</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-14/《芋道 Spring Cloud 链路追踪 SkyWalking》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/SkyWalking/?githubs>\n"
  },
  {
    "path": "labx-15/labx-15-admin-02-adminserver/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-15</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-15-admin-02-adminserver</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring Boot Admin Server 的自动化配置 -->\n        <!--\n            包含 1. spring-boot-admin-server ：Server 端\n                2. spring-boot-admin-server-ui ：UI 界面\n                3. spring-boot-admin-server-cloud ：对 Spring Cloud 的接入\n        -->\n        <dependency>\n            <groupId>de.codecentric</groupId>\n            <artifactId>spring-boot-admin-starter-server</artifactId>\n            <version>2.2.2</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-15/labx-15-admin-02-adminserver/src/main/java/cn/iocoder/springcloud/labx15/adminserver/AdminServerApplication.java",
    "content": "package cn.iocoder.springcloud.labx15.adminserver;\n\nimport de.codecentric.boot.admin.server.config.EnableAdminServer;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@EnableAdminServer\npublic class AdminServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AdminServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-15/labx-15-admin-02-adminserver/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-admin-server # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。\n"
  },
  {
    "path": "labx-15/labx-15-admin-02-demo-application/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-15</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-15-admin-02-demo-application</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-15/labx-15-admin-02-demo-application/src/main/java/cn/iocoder/springcloud/labx15/demo/Demo01Application.java",
    "content": "package cn.iocoder.springcloud.labx15.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Demo01Application {\n\n    public static void main(String[] args) {\n        System.setProperty(\"server.port\", \"18081\"); // 端口 18081\n        SpringApplication.run(Demo01Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-15/labx-15-admin-02-demo-application/src/main/java/cn/iocoder/springcloud/labx15/demo/Demo02Application.java",
    "content": "package cn.iocoder.springcloud.labx15.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Demo02Application {\n\n    public static void main(String[] args) {\n        System.setProperty(\"server.port\", \"18082\");  // 端口 18082\n        SpringApplication.run(Demo02Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-15/labx-15-admin-02-demo-application/src/main/java/cn/iocoder/springcloud/labx15/demo/Demo03Application.java",
    "content": "package cn.iocoder.springcloud.labx15.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Demo03Application {\n\n    public static void main(String[] args) throws InterruptedException {\n        System.setProperty(\"server.port\", \"18083\");  // 端口 18082\n        SpringApplication.run(Demo03Application.class, args);\n        Thread.sleep(1000000L);\n    }\n\n}\n"
  },
  {
    "path": "labx-15/labx-15-admin-02-demo-application/src/main/resources/application.yaml",
    "content": "management:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\nspring:\n  application:\n    name: demo-application # 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。\n"
  },
  {
    "path": "labx-15/labx-15-admin-03-adminserver/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-15</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-15-admin-03-adminserver</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring Boot Admin Server 的自动化配置 -->\n        <!--\n            包含 1. spring-boot-admin-server ：Server 端\n                2. spring-boot-admin-server-ui ：UI 界面\n                3. spring-boot-admin-server-cloud ：对 Spring Cloud 的接入\n        -->\n        <dependency>\n            <groupId>de.codecentric</groupId>\n            <artifactId>spring-boot-admin-starter-server</artifactId>\n            <version>2.2.2</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-15/labx-15-admin-03-adminserver/src/main/java/cn/iocoder/springcloud/labx15/adminserver/AdminServerApplication.java",
    "content": "package cn.iocoder.springcloud.labx15.adminserver;\n\nimport de.codecentric.boot.admin.server.config.EnableAdminServer;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@EnableAdminServer\npublic class AdminServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AdminServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-15/labx-15-admin-03-adminserver/src/main/java/cn/iocoder/springcloud/labx15/adminserver/config/SecurityConfig.java",
    "content": "package cn.iocoder.springcloud.labx15.adminserver.config;\n\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;\nimport org.springframework.security.config.web.server.ServerHttpSecurity;\nimport org.springframework.security.core.userdetails.MapReactiveUserDetailsService;\nimport org.springframework.security.core.userdetails.User;\nimport org.springframework.security.core.userdetails.UserDetails;\nimport org.springframework.security.web.server.SecurityWebFilterChain;\n\n@Configuration\n@EnableWebFluxSecurity // 开启 Security 对 WebFlux 的安全功能\npublic class SecurityConfig {\n\n    @Bean\n    public MapReactiveUserDetailsService userDetailsService() {\n        // 创建一个用户\n        UserDetails user = User.withDefaultPasswordEncoder()\n                .username(\"user\")\n                .password(\"user\")\n                .roles(\"USER\")\n                .build();\n\n        // 如果胖友有更多用户的诉求，这里可以继续创建\n\n        // 创建 MapReactiveUserDetailsService\n        return new MapReactiveUserDetailsService(user);\n    }\n\n    @Bean\n    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {\n        http.authorizeExchange(exchanges -> // 设置权限配置\n                exchanges\n                        .pathMatchers(\"/assets/**\").permitAll() // 静态资源，允许匿名访问\n                        .pathMatchers(\"/login\").permitAll() // 登陆接口，允许匿名访问\n                        .anyExchange().authenticated() //\n        )\n        .formLogin().loginPage(\"/login\") // 登陆页面\n        .and().logout().logoutUrl(\"/logout\") // 登出界面\n        .and().httpBasic() // HTTP Basic 认证方式\n        .and().csrf().disable(); // csrf 禁用\n        return http.build();\n    }\n\n}\n"
  },
  {
    "path": "labx-15/labx-15-admin-03-adminserver/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-admin-server # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。\n        metadata:\n          user.name: user # Spring Security 安全认证的账号\n          user.password: user # Spring Security 安全认证的密码\n"
  },
  {
    "path": "labx-15/labx-15-admin-03-demo-application/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-15</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-15-admin-03-demo-application</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-15/labx-15-admin-03-demo-application/src/main/java/cn/iocoder/springcloud/labx15/demo/Demo01Application.java",
    "content": "package cn.iocoder.springcloud.labx15.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Demo01Application {\n\n    public static void main(String[] args) {\n        System.setProperty(\"server.port\", \"18081\"); // 端口 18081\n        SpringApplication.run(Demo01Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-15/labx-15-admin-03-demo-application/src/main/java/cn/iocoder/springcloud/labx15/demo/Demo02Application.java",
    "content": "package cn.iocoder.springcloud.labx15.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class Demo02Application {\n\n    public static void main(String[] args) {\n        System.setProperty(\"server.port\", \"18082\");  // 端口 18082\n        SpringApplication.run(Demo02Application.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-15/labx-15-admin-03-demo-application/src/main/resources/application.yaml",
    "content": "management:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\nspring:\n  application:\n    name: demo-application # 应用名\n\n  # Spring Security 配置项，对应 SecurityProperties 配置类\n  security:\n    # 配置默认的 InMemoryUserDetailsManager 的用户账号与密码。\n    user:\n      name: test # 账号\n      password: test # 密码\n\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。\n        metadata:\n          user.name: ${spring.security.user.name} # Spring Security 认证账号\n          user.password: ${spring.security.user.password} # Spring Security 认证密码\n"
  },
  {
    "path": "labx-15/labx-15-admin-04-adminserver-custom/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-15</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-15-admin-04-adminserver-custom</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring Boot Admin Server 的自动化配置 -->\n        <!--\n            包含 1. spring-boot-admin-server ：Server 端\n                2. spring-boot-admin-server-ui ：UI 界面\n                3. spring-boot-admin-server-cloud ：对 Spring Cloud 的接入\n        -->\n        <dependency>\n            <groupId>de.codecentric</groupId>\n            <artifactId>spring-boot-admin-starter-server</artifactId>\n            <version>2.2.2</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-15/labx-15-admin-04-adminserver-custom/src/main/java/cn/iocoder/springcloud/labx15/adminserver/AdminServerApplication.java",
    "content": "package cn.iocoder.springcloud.labx15.adminserver;\n\nimport de.codecentric.boot.admin.server.config.EnableAdminServer;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@EnableAdminServer\npublic class AdminServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AdminServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-15/labx-15-admin-04-adminserver-custom/src/main/java/cn/iocoder/springcloud/labx15/adminserver/notify/LoggerNotifier.java",
    "content": "package cn.iocoder.springcloud.labx15.adminserver.notify;\n\nimport de.codecentric.boot.admin.server.domain.entities.Instance;\nimport de.codecentric.boot.admin.server.domain.entities.InstanceRepository;\nimport de.codecentric.boot.admin.server.domain.events.InstanceEvent;\nimport de.codecentric.boot.admin.server.domain.events.InstanceStatusChangedEvent;\nimport de.codecentric.boot.admin.server.notify.AbstractEventNotifier;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\nimport reactor.core.publisher.Mono;\n\n@Component\npublic class LoggerNotifier extends AbstractEventNotifier {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    public LoggerNotifier(InstanceRepository repository) {\n        super(repository);\n    }\n\n    @Override\n    protected Mono<Void> doNotify(InstanceEvent event, Instance instance) {\n        return Mono.fromRunnable(() -> {\n            if (event instanceof InstanceStatusChangedEvent) {\n                logger.info(\"Instance {} ({}) is {}\", instance.getRegistration().getName(), event.getInstance(),\n                        ((InstanceStatusChangedEvent) event).getStatusInfo().getStatus());\n            } else {\n                logger.info(\"Instance {} ({}) {}\", instance.getRegistration().getName(), event.getInstance(), event.getType());\n            }\n        });\n    }\n\n}\n"
  },
  {
    "path": "labx-15/labx-15-admin-04-adminserver-custom/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-admin-server # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。\n"
  },
  {
    "path": "labx-15/labx-15-admin-04-adminserver-mail/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-15</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-15-admin-04-adminserver-mail</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring Boot Admin Server 的自动化配置 -->\n        <!--\n            包含 1. spring-boot-admin-server ：Server 端\n                2. spring-boot-admin-server-ui ：UI 界面\n                3. spring-boot-admin-server-cloud ：对 Spring Cloud 的接入\n        -->\n        <dependency>\n            <groupId>de.codecentric</groupId>\n            <artifactId>spring-boot-admin-starter-server</artifactId>\n            <version>2.2.2</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 实现对 Java Mail 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-mail</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-15/labx-15-admin-04-adminserver-mail/src/main/java/cn/iocoder/springcloud/labx15/adminserver/AdminServerApplication.java",
    "content": "package cn.iocoder.springcloud.labx15.adminserver;\n\nimport de.codecentric.boot.admin.server.config.EnableAdminServer;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n@EnableAdminServer\npublic class AdminServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AdminServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-15/labx-15-admin-04-adminserver-mail/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-admin-server # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        service: ${spring.application.name} # 注册到 Nacos 的服务名。默认值为 ${spring.application.name}。\n\n  mail: # 配置发送告警的邮箱\n    host: smtp.126.com\n    username: wwbmlhh@126.com\n    password: '******'\n    default-encoding: UTF-8\n\n  boot:\n    admin:\n      notify:\n        mail:\n          from: ${spring.mail.username} # 告警发件人\n          to: 7685413@qq.com # 告警收件人\n"
  },
  {
    "path": "labx-15/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-15</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-15-admin-02-adminserver</module>\n        <module>labx-15-admin-02-demo-application</module>\n\n        <module>labx-15-admin-03-adminserver</module>\n        <module>labx-15-admin-03-demo-application</module>\n\n        <module>labx-15-admin-04-adminserver-mail</module>\n        <module>labx-15-admin-04-adminserver-custom</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-15/《芋道 Spring Cloud 监控工具 Admin 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/SB-Admin/?github>\n"
  },
  {
    "path": "labx-16/deploy.sh",
    "content": "#!/bin/bash\nset -e\n\n# 基础\n# export JAVA_HOME=/work/programs/jdk/jdk1.8.0_181\n# export PATH=PATH=$PATH:$JAVA_HOME/bin\n# export CLASSPATH=$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar\n\nDATE=$(date +%Y%m%d%H%M)\n# 基础路径\nBASE_PATH=/work/projects/labx-16-demo-01\n# 编译后 jar 的地址。部署时，Jenkins 会上传 jar 包到该目录下\nSOURCE_PATH=$BASE_PATH/build\n# 服务名称。同时约定部署服务的 jar 包名字也为它。\nSERVER_NAME=labx-16-demo-01\n# 环境\nPROFILES_ACTIVE=prod\n# 健康检查 URL\nHEALTH_CHECK_URL=http://127.0.0.1:8078/actuator/health/\n# 修改状态 URL\nSTATUS_URL=http://127.0.0.1:8078/actuator/service-registry/\n\n# heapError 存放路径\nHEAP_ERROR_PATH=$BASE_PATH/heapError\n# JVM 参数\nJAVA_OPS=\"-Xms1024m -Xmx1024m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=$HEAP_ERROR_PATH\"\n# JavaAgent 参数。可用于配置 SkyWalking 等链路追踪\nJAVA_AGENT=\n\n# 备份\nfunction backup() {\n    # 如果不存在，则无需备份\n    if [ ! -f \"$BASE_PATH/$SERVER_NAME.jar\" ]; then\n        echo \"[backup] $BASE_PATH/$SERVER_NAME.jar 不存在，跳过备份\"\n    # 如果存在，则备份到 backup 目录下，使用时间作为后缀\n    else\n        echo \"[backup] 开始备份 $SERVER_NAME ...\"\n        cp $BASE_PATH/$SERVER_NAME.jar $BASE_PATH/backup/$SERVER_NAME-$DATE.jar\n        echo \"[backup] 备份 $SERVER_NAME 完成\"\n    fi\n}\n\n# 最新构建代码 移动到项目环境\nfunction transfer() {\n    echo \"[transfer] 开始转移 $SERVER_NAME.jar\"\n\n    # 删除原 jar 包\n    if [ ! -f \"$BASE_PATH/$SERVER_NAME.jar\" ]; then\n        echo \"[transfer] $BASE_PATH/$SERVER_NAME.jar 不存在，跳过删除\"\n    else\n        echo \"[transfer] 移除 $BASE_PATH/$SERVER_NAME.jar 完成\"\n        rm $BASE_PATH/$SERVER_NAME.jar\n    fi\n\n    # 复制新 jar 包\n    echo \"[transfer] 从 $SOURCE_PATH 中获取 $SERVER_NAME.jar 并迁移至 $BASE_PATH ....\"\n    cp $SOURCE_PATH/$SERVER_NAME.jar $BASE_PATH\n\n    echo \"[transfer] 转移 $SERVER_NAME.jar 完成\"\n}\n\n# 停止\nfunction stop() {\n    echo \"[stop] 开始停止 $BASE_PATH/$SERVER_NAME\"\n    PID=$(ps -ef | grep $BASE_PATH/$SERVER_NAME | grep -v \"grep\" | awk '{print $2}')\n    # 如果 Java 服务启动中，则进行关闭\n    if [ -n \"$PID\" ]; then\n        # 从注册中心下线\n        echo \"[stop] 从注册中心下线当前实例，并 sleep 20 秒\"\n        curl -X POST $STATUS_URL -d '{\"status\": \"DOWN\"}' -H 'content-type: application/json'\n        sleep 20\n        # 正常关闭\n        echo \"[stop] $BASE_PATH/$SERVER_NAME 运行中，开始 kill [$PID]\"\n        kill -15 $PID\n        # 等待最大 60 秒，直到关闭完成。\n        for ((i = 0; i < 60; i++))\n            do\n                sleep 1\n                PID=$(ps -ef | grep $BASE_PATH/$SERVER_NAME | grep -v \"grep\" | awk '{print $2}')\n                if [ -n \"$PID\" ]; then\n                    echo -e \".\\c\"\n                else\n                    echo '[stop] 停止 $BASE_PATH/$SERVER_NAME 成功'\n                    break\n                fi\n\t\t    done\n\n        # 如果正常关闭失败，那么进行强制 kill -9 进行关闭\n        if [ -n \"$PID\" ]; then\n            echo \"[stop] $BASE_PATH/$SERVER_NAME 失败，强制 kill -9 $PID\"\n            kill -9 $PID\n        fi\n    # 如果 Java 服务未启动，则无需关闭\n    else\n        echo \"[stop] $BASE_PATH/$SERVER_NAME 未启动，无需停止\"\n    fi\n}\n\n# 启动\nfunction start() {\n    # 开启启动前，打印启动参数\n    echo \"[start] 开始启动 $BASE_PATH/$SERVER_NAME\"\n    echo \"[start] JAVA_OPS: $JAVA_OPS\"\n    echo \"[start] JAVA_AGENT: $JAVA_AGENT\"\n    echo \"[start] PROFILES: $PROFILES_ACTIVE\"\n\n    # 开始启动\n    BUILD_ID=dontKillMe nohup java -server $JAVA_OPS $JAVA_AGENT -jar $BASE_PATH/$SERVER_NAME.jar --spring.profiles.active=$PROFILES_ACTIVE &\n    echo \"[start] 启动 $BASE_PATH/$SERVER_NAME 完成\"\n}\n\n# 健康检查\nfunction healthCheck() {\n    # 如果配置健康检查，则进行健康检查\n    if [ -n \"$HEALTH_CHECK_URL\" ]; then\n        # 健康检查最大 60 秒，直到健康检查通过\n        echo \"[healthCheck] 开始通过 $HEALTH_CHECK_URL 地址，进行健康检查\";\n        for ((i = 0; i < 60; i++))\n            do\n                # 请求健康检查地址，只获取状态码。\n                result=`curl -I -m 10 -o /dev/null -s -w %{http_code} $HEALTH_CHECK_URL || echo \"000\"`\n                # 如果状态码为 200，则说明健康检查通过\n                if [ \"$result\" == \"200\" ]; then\n                    echo \"[healthCheck] 健康检查通过\";\n                    break\n                # 如果状态码非 200，则说明未通过。sleep 1 秒后，继续重试\n                else\n                    echo -e \".\\c\"\n                    sleep 1\n                fi\n            done\n\n        # 健康检查未通过，则异常退出 shell 脚本，不继续部署。\n        if [ ! \"$result\" == \"200\" ]; then\n            echo \"[healthCheck] 健康检查不通过，可能部署失败。查看日志，自行判断是否启动成功\";\n            tail -n 10 nohup.out\n            exit 1;\n        # 健康检查通过，打印最后 10 行日志，可能部署的人想看下日志。\n        else\n            tail -n 10 nohup.out\n        fi\n    # 如果未配置健康检查，则 slepp 60 秒，人工看日志是否部署成功。\n    else\n        echo \"[healthCheck] HEALTH_CHECK_URL 未配置，开始 sleep 60 秒\";\n        sleep 60\n        echo \"[healthCheck] sleep 60 秒完成，查看日志，自行判断是否启动成功\";\n        tail -n 50 nohup.out\n    fi\n}\n\n# 部署\nfunction deploy() {\n    cd $BASE_PATH\n    # 备份原 jar\n    backup\n    # 停止 Java 服务\n    stop\n    # 部署新 jar\n    transfer\n    # 启动 Java 服务\n    start\n    # 健康检查\n    healthCheck\n}\n\ndeploy\n"
  },
  {
    "path": "labx-16/labx-16-demo-01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-16</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-16-demo-01</artifactId>\n<!--    <version>1.0.0</version>-->\n    <packaging>jar</packaging>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <!-- 设置构建的 jar 包名 -->\n        <finalName>${project.artifactId}</finalName>\n        <!-- 使用 spring-boot-maven-plugin 插件打包 -->\n        <plugins>\n            <plugin>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-maven-plugin</artifactId>\n                <version>${spring.boot.version}</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>repackage</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "labx-16/labx-16-demo-01/src/main/java/cn/iocoder/springcloud/lab16/jenkinsdemo/Application.java",
    "content": "package cn.iocoder.springcloud.lab16.jenkinsdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.ApplicationEvent;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.event.ContextClosedEvent;\nimport org.springframework.stereotype.Component;\n\n@SpringBootApplication\npublic class Application {\n\n    public static void main(String[] args) {\n        SpringApplication.run(Application.class, args);\n    }\n\n    @Component\n    public class Listener implements ApplicationListener<ApplicationEvent> {\n\n        @Override\n        public void onApplicationEvent(ApplicationEvent event) {\n            if (event instanceof ContextClosedEvent) {\n                this.sleep(10);\n            }\n        }\n\n        private void sleep(int seconds) {\n            try {\n                Thread.sleep(seconds * 1000L);\n            } catch (InterruptedException ignore) {\n            }\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-16/labx-16-demo-01/src/main/java/cn/iocoder/springcloud/lab16/jenkinsdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.lab16.jenkinsdemo.controller;\n\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @GetMapping(\"/echo\")\n    public String echo() {\n        return \"echo\";\n    }\n\n}\n"
  },
  {
    "path": "labx-16/labx-16-demo-01/src/main/resources/application-dev.yaml",
    "content": "server:\n  port: 8079\n\nmanagement:\n  server:\n    port: 8078 # 自定义端口，避免 Nginx 暴露出去\n\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\nspring:\n  application:\n    name: demo-service\n\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\n"
  },
  {
    "path": "labx-16/labx-16-demo-01/src/main/resources/application-local.yaml",
    "content": "server:\n  port: 8079\n\nmanagement:\n  server:\n    port: 8078 # 自定义端口，避免 Nginx 暴露出去\n\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\nspring:\n  application:\n    name: demo-service\n\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n"
  },
  {
    "path": "labx-16/labx-16-demo-01/src/main/resources/application-pre.yaml",
    "content": "server:\n  port: 8079\n\nmanagement:\n  server:\n    port: 8078 # 自定义端口，避免 Nginx 暴露出去\n\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\nspring:\n  application:\n    name: demo-service\n\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n"
  },
  {
    "path": "labx-16/labx-16-demo-01/src/main/resources/application-prod.yaml",
    "content": "server:\n  port: 8079\n\nmanagement:\n  server:\n    port: 8078 # 自定义端口，避免 Nginx 暴露出去\n\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\nspring:\n  application:\n    name: demo-service\n\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n"
  },
  {
    "path": "labx-16/labx-16-demo-01/src/main/resources/application-uat.yaml",
    "content": "server:\n  port: 8079\n\nmanagement:\n  server:\n    port: 8078 # 自定义端口，避免 Nginx 暴露出去\n\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\nspring:\n  application:\n    name: demo-service\n\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n"
  },
  {
    "path": "labx-16/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-16</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-16-demo-01</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-16/《芋道 Spring Cloud 持续交付 Jenkins 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/Jenkins/?github>\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/data.sql",
    "content": "# Order\nDROP DATABASE IF EXISTS seata_order;\nCREATE DATABASE seata_order;\n\nCREATE TABLE seata_order.orders\n(\n    id               INT(11) NOT NULL AUTO_INCREMENT,\n    user_id          INT(11)        DEFAULT NULL,\n    product_id       INT(11)        DEFAULT NULL,\n    pay_amount       DECIMAL(10, 0) DEFAULT NULL,\n    add_time         DATETIME       DEFAULT CURRENT_TIMESTAMP,\n    last_update_time DATETIME       DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\n\nCREATE TABLE seata_order.undo_log\n(\n    id            BIGINT(20)   NOT NULL AUTO_INCREMENT,\n    branch_id     BIGINT(20)   NOT NULL,\n    xid           VARCHAR(100) NOT NULL,\n    context       VARCHAR(128) NOT NULL,\n    rollback_info LONGBLOB     NOT NULL,\n    log_status    INT(11)      NOT NULL,\n    log_created   DATETIME     NOT NULL,\n    log_modified  DATETIME     NOT NULL,\n    PRIMARY KEY (id),\n    UNIQUE KEY ux_undo_log (xid, branch_id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\n\n# Product\nDROP DATABASE IF EXISTS seata_product;\nCREATE DATABASE seata_product;\n\nCREATE TABLE seata_product.product\n(\n    id               INT(11) NOT NULL AUTO_INCREMENT,\n    stock            INT(11)  DEFAULT NULL,\n    last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\nINSERT INTO seata_product.product (id, stock) VALUES (1, 10); # 插入一条产品的库存\n\nCREATE TABLE seata_product.undo_log\n(\n    id            BIGINT(20)   NOT NULL AUTO_INCREMENT,\n    branch_id     BIGINT(20)   NOT NULL,\n    xid           VARCHAR(100) NOT NULL,\n    context       VARCHAR(128) NOT NULL,\n    rollback_info LONGBLOB     NOT NULL,\n    log_status    INT(11)      NOT NULL,\n    log_created   DATETIME     NOT NULL,\n    log_modified  DATETIME     NOT NULL,\n    PRIMARY KEY (id),\n    UNIQUE KEY ux_undo_log (xid, branch_id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\n\n# Account\nDROP DATABASE IF EXISTS seata_account;\nCREATE DATABASE seata_account;\n\nCREATE TABLE seata_account.account\n(\n    id               INT(11) NOT NULL AUTO_INCREMENT,\n    balance          DOUBLE   DEFAULT NULL,\n    last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1  DEFAULT CHARSET = utf8;\n\nCREATE TABLE seata_account.undo_log\n(\n    id            BIGINT(20)   NOT NULL AUTO_INCREMENT,\n    branch_id     BIGINT(20)   NOT NULL,\n    xid           VARCHAR(100) NOT NULL,\n    context       VARCHAR(128) NOT NULL,\n    rollback_info LONGBLOB     NOT NULL,\n    log_status    INT(11)      NOT NULL,\n    log_created   DATETIME     NOT NULL,\n    log_modified  DATETIME     NOT NULL,\n    PRIMARY KEY (id),\n    UNIQUE KEY ux_undo_log (xid, branch_id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\nINSERT INTO seata_account.account (id, balance) VALUES (1, 10);\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-account-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-17-sc-seata-at-feign-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-17-sc-seata-at-feign-demo-account-service</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>druid-spring-boot-starter</artifactId>\n            <version>1.1.10</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.2</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Seata 相关依赖，使用 Seata 实现分布式事务，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-alibaba-seata</artifactId>\n        </dependency>\n        <dependency> <!-- 主要想使用 seata 1.1.0 版本 -->\n            <groupId>io.seata</groupId>\n            <artifactId>seata-spring-boot-starter</artifactId>\n            <version>1.1.0</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-account-service/src/main/java/cn/iocoder/springcloud/labx17/accountservice/AccountServiceApplication.java",
    "content": "package cn.iocoder.springcloud.labx17.accountservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class    AccountServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AccountServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-account-service/src/main/java/cn/iocoder/springcloud/labx17/accountservice/controller/AccountController.java",
    "content": "package cn.iocoder.springcloud.labx17.accountservice.controller;\n\nimport cn.iocoder.springcloud.labx17.accountservice.dto.AccountReduceBalanceDTO;\nimport cn.iocoder.springcloud.labx17.accountservice.service.AccountService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/account\")\npublic class AccountController {\n\n    private Logger logger = LoggerFactory.getLogger(AccountController.class);\n\n    @Autowired\n    private AccountService accountService;\n\n    @PostMapping(\"/reduce-balance\")\n    public void reduceBalance(@RequestBody AccountReduceBalanceDTO accountReduceBalanceDTO) throws Exception {\n        logger.info(\"[reduceBalance] 收到减少余额请求, 用户:{}, 金额:{}\", accountReduceBalanceDTO.getUserId(),\n                accountReduceBalanceDTO.getPrice());\n        accountService.reduceBalance(accountReduceBalanceDTO.getUserId(), accountReduceBalanceDTO.getPrice());\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-account-service/src/main/java/cn/iocoder/springcloud/labx17/accountservice/dao/AccountDao.java",
    "content": "package cn.iocoder.springcloud.labx17.accountservice.dao;\n\n\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\nimport org.apache.ibatis.annotations.Update;\nimport org.springframework.stereotype.Repository;\n\n@Mapper\n@Repository\npublic interface AccountDao {\n\n    /**\n     * 获取账户余额\n     *\n     * @param userId 用户 ID\n     * @return 账户余额\n     */\n    @Select(\"SELECT balance FROM account WHERE id = #{userId}\")\n    Integer getBalance(@Param(\"userId\") Long userId);\n\n    /**\n     * 扣减余额\n     *\n     * @param price 需要扣减的数目\n     * @return 影响记录行数\n     */\n    @Update(\"UPDATE account SET balance = balance - #{price} WHERE id = 1 AND balance >= ${price}\")\n    int reduceBalance(@Param(\"price\") Integer price);\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-account-service/src/main/java/cn/iocoder/springcloud/labx17/accountservice/dto/AccountReduceBalanceDTO.java",
    "content": "package cn.iocoder.springcloud.labx17.accountservice.dto;\n\n/**\n * 账户减少余额 DTO\n */\npublic class AccountReduceBalanceDTO {\n\n    /**\n     * 用户编号\n     */\n    private Long userId;\n\n    /**\n     * 扣减金额\n     */\n    private Integer price;\n\n    public Long getUserId() {\n        return userId;\n    }\n\n    public AccountReduceBalanceDTO setUserId(Long userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    public Integer getPrice() {\n        return price;\n    }\n\n    public AccountReduceBalanceDTO setPrice(Integer price) {\n        this.price = price;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-account-service/src/main/java/cn/iocoder/springcloud/labx17/accountservice/service/AccountService.java",
    "content": "package cn.iocoder.springcloud.labx17.accountservice.service;\n\n/**\n * 账户 Service\n */\npublic interface AccountService {\n\n    /**\n     * 扣除余额\n     *\n     * @param userId 用户编号\n     * @param price  扣减金额\n     * @throws Exception 失败时抛出异常\n     */\n    void reduceBalance(Long userId, Integer price) throws Exception;\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-account-service/src/main/java/cn/iocoder/springcloud/labx17/accountservice/service/impl/AccountServiceImpl.java",
    "content": "package cn.iocoder.springcloud.labx17.accountservice.service.impl;\n\nimport cn.iocoder.springcloud.labx17.accountservice.dao.AccountDao;\nimport cn.iocoder.springcloud.labx17.accountservice.service.AccountService;\nimport io.seata.core.context.RootContext;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\n@Service\npublic class AccountServiceImpl implements AccountService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private AccountDao accountDao;\n\n    @Override\n    @Transactional // 开启新事物\n    public void reduceBalance(Long userId, Integer price) throws Exception {\n        logger.info(\"[reduceBalance] 当前 XID: {}\", RootContext.getXID());\n\n        // 检查余额\n        checkBalance(userId, price);\n\n        logger.info(\"[reduceBalance] 开始扣减用户 {} 余额\", userId);\n        // 扣除余额\n        int updateCount = accountDao.reduceBalance(price);\n        // 扣除成功\n        if (updateCount == 0) {\n            logger.warn(\"[reduceBalance] 扣除用户 {} 余额失败\", userId);\n            throw new Exception(\"余额不足\");\n        }\n        logger.info(\"[reduceBalance] 扣除用户 {} 余额成功\", userId);\n    }\n\n    private void checkBalance(Long userId, Integer price) throws Exception {\n        logger.info(\"[checkBalance] 检查用户 {} 余额\", userId);\n        Integer balance = accountDao.getBalance(userId);\n        if (balance < price) {\n            logger.warn(\"[checkBalance] 用户 {} 余额不足，当前余额:{}\", userId, balance);\n            throw new Exception(\"余额不足\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-account-service/src/main/resources/application-file.yaml",
    "content": "server:\n  port: 8083\n\nspring:\n  application:\n    name: account-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      account-service-group: default\n    # 分组和 Seata 服务的映射\n    grouplist:\n      default: 127.0.0.1:8091\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-account-service/src/main/resources/application.yaml",
    "content": "server:\n  port: 8083\n\nspring:\n  application:\n    name: account-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # Seata 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      account-service-group: default\n  # Seata 注册中心配置项，对应 RegistryProperties 类\n  registry:\n    type: nacos # 注册中心类型，默认为 file\n    nacos:\n      cluster: default # 使用的 Seata 分组\n      namespace: # Nacos 命名空间\n      serverAddr: localhost # Nacos 服务地址\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-order-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-17-sc-seata-at-feign-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-17-sc-seata-at-feign-demo-order-service</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>druid-spring-boot-starter</artifactId>\n            <version>1.1.10</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.2</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Seata 相关依赖，使用 Seata 实现分布式事务，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-alibaba-seata</artifactId>\n        </dependency>\n        <dependency> <!-- 主要想使用 seata 1.1.0 版本 -->\n            <groupId>io.seata</groupId>\n            <artifactId>seata-spring-boot-starter</artifactId>\n            <version>1.1.0</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud OpenFeign 相关依赖，使用 OpenFeign 提供声明式调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-order-service/src/main/java/cn/iocoder/springcloud/labx17/orderservice/OrderServiceApplication.java",
    "content": "package cn.iocoder.springcloud.labx17.orderservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\n\n@SpringBootApplication\n@EnableFeignClients\npublic class OrderServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(OrderServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-order-service/src/main/java/cn/iocoder/springcloud/labx17/orderservice/controller/OrderController.java",
    "content": "package cn.iocoder.springcloud.labx17.orderservice.controller;\n\nimport cn.iocoder.springcloud.labx17.orderservice.service.OrderService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/order\")\npublic class OrderController {\n\n    private Logger logger = LoggerFactory.getLogger(OrderController.class);\n\n    @Autowired\n    private OrderService orderService;\n\n    @PostMapping(\"/create\")\n    public Integer createOrder(@RequestParam(\"userId\") Long userId,\n                               @RequestParam(\"productId\") Long productId,\n                               @RequestParam(\"price\") Integer price) throws Exception {\n        logger.info(\"[createOrder] 收到下单请求,用户:{}, 商品:{}, 价格:{}\", userId, productId, price);\n        return orderService.createOrder(userId, productId, price);\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-order-service/src/main/java/cn/iocoder/springcloud/labx17/orderservice/dao/OrderDao.java",
    "content": "package cn.iocoder.springcloud.labx17.orderservice.dao;\n\nimport cn.iocoder.springcloud.labx17.orderservice.entity.OrderDO;\nimport org.apache.ibatis.annotations.Insert;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Options;\nimport org.springframework.stereotype.Repository;\n\n@Mapper\n@Repository\npublic interface OrderDao {\n\n    /**\n     * 插入订单记录\n     *\n     * @param order 订单\n     * @return 影响记录数量\n     */\n    @Insert(\"INSERT INTO orders (user_id, product_id, pay_amount) VALUES (#{userId}, #{productId}, #{payAmount})\")\n    @Options(useGeneratedKeys = true, keyColumn = \"id\", keyProperty = \"id\")\n    int saveOrder(OrderDO order);\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-order-service/src/main/java/cn/iocoder/springcloud/labx17/orderservice/entity/OrderDO.java",
    "content": "package cn.iocoder.springcloud.labx17.orderservice.entity;\n\n/**\n * 订单实体\n */\npublic class OrderDO {\n\n    /** 订单编号 **/\n    private Integer id;\n\n    /** 用户编号 **/\n    private Long userId;\n\n    /** 产品编号 **/\n    private Long productId;\n\n    /** 支付金额 **/\n    private Integer payAmount;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public OrderDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Long getUserId() {\n        return userId;\n    }\n\n    public OrderDO setUserId(Long userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    public Long getProductId() {\n        return productId;\n    }\n\n    public OrderDO setProductId(Long productId) {\n        this.productId = productId;\n        return this;\n    }\n\n    public Integer getPayAmount() {\n        return payAmount;\n    }\n\n    public OrderDO setPayAmount(Integer payAmount) {\n        this.payAmount = payAmount;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-order-service/src/main/java/cn/iocoder/springcloud/labx17/orderservice/feign/AccountServiceFeignClient.java",
    "content": "package cn.iocoder.springcloud.labx17.orderservice.feign;\n\nimport cn.iocoder.springcloud.labx17.orderservice.feign.dto.AccountReduceBalanceDTO;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\n\n/**\n * `account-service` 服务的 Feign 客户端\n */\n@FeignClient(name = \"account-service\")\npublic interface AccountServiceFeignClient {\n\n    @PostMapping(\"/account/reduce-balance\")\n    void reduceBalance(@RequestBody AccountReduceBalanceDTO accountReduceBalanceDTO);\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-order-service/src/main/java/cn/iocoder/springcloud/labx17/orderservice/feign/ProductServiceFeignClient.java",
    "content": "package cn.iocoder.springcloud.labx17.orderservice.feign;\n\nimport cn.iocoder.springcloud.labx17.orderservice.feign.dto.ProductReduceStockDTO;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\n\n/**\n * `product-service` 服务的 Feign 客户端\n */\n@FeignClient(name = \"product-service\")\npublic interface ProductServiceFeignClient {\n\n    @PostMapping(\"/product/reduce-stock\")\n    void reduceStock(@RequestBody ProductReduceStockDTO productReduceStockDTO);\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-order-service/src/main/java/cn/iocoder/springcloud/labx17/orderservice/feign/dto/AccountReduceBalanceDTO.java",
    "content": "package cn.iocoder.springcloud.labx17.orderservice.feign.dto;\n\n/**\n * 账户减少余额 DTO\n */\npublic class AccountReduceBalanceDTO {\n\n    /**\n     * 用户编号\n     */\n    private Long userId;\n\n    /**\n     * 扣减金额\n     */\n    private Integer price;\n\n    public Long getUserId() {\n        return userId;\n    }\n\n    public AccountReduceBalanceDTO setUserId(Long userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    public Integer getPrice() {\n        return price;\n    }\n\n    public AccountReduceBalanceDTO setPrice(Integer price) {\n        this.price = price;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-order-service/src/main/java/cn/iocoder/springcloud/labx17/orderservice/feign/dto/ProductReduceStockDTO.java",
    "content": "package cn.iocoder.springcloud.labx17.orderservice.feign.dto;\n\n/**\n * 商品减少库存 DTO\n */\npublic class ProductReduceStockDTO {\n\n    /**\n     * 商品编号\n     */\n    private Long productId;\n    /**\n     * 数量\n     */\n    private Integer amount;\n\n    public Long getProductId() {\n        return productId;\n    }\n\n    public ProductReduceStockDTO setProductId(Long productId) {\n        this.productId = productId;\n        return this;\n    }\n\n    public Integer getAmount() {\n        return amount;\n    }\n\n    public ProductReduceStockDTO setAmount(Integer amount) {\n        this.amount = amount;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-order-service/src/main/java/cn/iocoder/springcloud/labx17/orderservice/service/OrderService.java",
    "content": "package cn.iocoder.springcloud.labx17.orderservice.service;\n\n/**\n * 订单 Service\n */\npublic interface OrderService {\n\n    /**\n     * 创建订单\n     *\n     * @param userId 用户编号\n     * @param productId 产品编号\n     * @param price 价格\n     * @return 订单编号\n     * @throws Exception 创建订单失败，抛出异常\n     */\n    Integer createOrder(Long userId, Long productId, Integer price) throws Exception;\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-order-service/src/main/java/cn/iocoder/springcloud/labx17/orderservice/service/impl/OrderServiceImpl.java",
    "content": "package cn.iocoder.springcloud.labx17.orderservice.service.impl;\n\nimport cn.iocoder.springcloud.labx17.orderservice.dao.OrderDao;\nimport cn.iocoder.springcloud.labx17.orderservice.entity.OrderDO;\nimport cn.iocoder.springcloud.labx17.orderservice.feign.*;\nimport cn.iocoder.springcloud.labx17.orderservice.feign.dto.*;\nimport cn.iocoder.springcloud.labx17.orderservice.service.OrderService;\nimport io.seata.core.context.RootContext;\nimport io.seata.spring.annotation.GlobalTransactional;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class OrderServiceImpl implements OrderService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private OrderDao orderDao;\n\n    @Autowired\n    private AccountServiceFeignClient accountService;\n    @Autowired\n    private ProductServiceFeignClient productService;\n\n    @Override\n    @GlobalTransactional\n    public Integer createOrder(Long userId, Long productId, Integer price) {\n        Integer amount = 1; // 购买数量，暂时设置为 1。\n\n        logger.info(\"[createOrder] 当前 XID: {}\", RootContext.getXID());\n\n        // 扣减库存\n        productService.reduceStock(new ProductReduceStockDTO().setProductId(productId).setAmount(amount));\n\n        // 扣减余额\n        accountService.reduceBalance(new AccountReduceBalanceDTO().setUserId(userId).setPrice(price));\n\n        // 保存订单\n        OrderDO order = new OrderDO().setUserId(userId).setProductId(productId).setPayAmount(amount * price);\n        orderDao.saveOrder(order);\n        logger.info(\"[createOrder] 保存订单: {}\", order.getId());\n\n        // 返回订单编号\n        return order.getId();\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-order-service/src/main/resources/application-file.yaml",
    "content": "server:\n  port: 8081 # 端口\n\nspring:\n  application:\n    name: order-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      order-service-group: default\n    # 分组和 Seata 服务的映射\n    grouplist:\n      default: 127.0.0.1:8091\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-order-service/src/main/resources/application.yaml",
    "content": "server:\n  port: 8081 # 端口\n\nspring:\n  application:\n    name: order-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # Seata 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      order-service-group: default\n  # Seata 注册中心配置项，对应 RegistryProperties 类\n  registry:\n    type: nacos # 注册中心类型，默认为 file\n    nacos:\n      cluster: default # 使用的 Seata 分组\n      namespace: # Nacos 命名空间\n      serverAddr: localhost # Nacos 服务地址\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-product-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-17-sc-seata-at-feign-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-17-sc-seata-at-feign-demo-product-service</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>druid-spring-boot-starter</artifactId>\n            <version>1.1.10</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.2</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Seata 相关依赖，使用 Seata 实现分布式事务，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-alibaba-seata</artifactId>\n        </dependency>\n        <dependency> <!-- 主要想使用 seata 1.1.0 版本 -->\n            <groupId>io.seata</groupId>\n            <artifactId>seata-spring-boot-starter</artifactId>\n            <version>1.1.0</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-product-service/src/main/java/cn/iocoder/springcloud/labx17/productservice/ProductServiceApplication.java",
    "content": "package cn.iocoder.springcloud.labx17.productservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProductServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProductServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-product-service/src/main/java/cn/iocoder/springcloud/labx17/productservice/controller/ProductController.java",
    "content": "package cn.iocoder.springcloud.labx17.productservice.controller;\n\nimport cn.iocoder.springcloud.labx17.productservice.dto.ProductReduceStockDTO;\nimport cn.iocoder.springcloud.labx17.productservice.service.ProductService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/product\")\npublic class ProductController {\n\n    private Logger logger = LoggerFactory.getLogger(ProductController.class);\n\n    @Autowired\n    private ProductService productService;\n\n    @PostMapping(\"/reduce-stock\")\n    public void reduceStock(@RequestBody ProductReduceStockDTO productReduceStockDTO)\n            throws Exception {\n        logger.info(\"[reduceStock] 收到减少库存请求, 商品:{}, 价格:{}\", productReduceStockDTO.getProductId(),\n                productReduceStockDTO.getAmount());\n        productService.reduceStock(productReduceStockDTO.getProductId(), productReduceStockDTO.getAmount());\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-product-service/src/main/java/cn/iocoder/springcloud/labx17/productservice/dao/ProductDao.java",
    "content": "package cn.iocoder.springcloud.labx17.productservice.dao;\n\n\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\nimport org.apache.ibatis.annotations.Update;\nimport org.springframework.stereotype.Repository;\n\n@Mapper\n@Repository\npublic interface ProductDao {\n\n    /**\n     * 获取库存\n     *\n     * @param productId 商品编号\n     * @return 库存\n     */\n    @Select(\"SELECT stock FROM product WHERE id = #{productId}\")\n    Integer getStock(@Param(\"productId\") Long productId);\n\n    /**\n     * 扣减库存\n     *\n     * @param productId 商品编号\n     * @param amount    扣减数量\n     * @return 影响记录行数\n     */\n    @Update(\"UPDATE product SET stock = stock - #{amount} WHERE id = #{productId} AND stock >= #{amount}\")\n    int reduceStock(@Param(\"productId\") Long productId, @Param(\"amount\") Integer amount);\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-product-service/src/main/java/cn/iocoder/springcloud/labx17/productservice/dto/ProductReduceStockDTO.java",
    "content": "package cn.iocoder.springcloud.labx17.productservice.dto;\n\n/**\n * 商品减少库存 DTO\n */\npublic class ProductReduceStockDTO {\n\n    /**\n     * 商品编号\n     */\n    private Long productId;\n    /**\n     * 数量\n     */\n    private Integer amount;\n\n    public Long getProductId() {\n        return productId;\n    }\n\n    public ProductReduceStockDTO setProductId(Long productId) {\n        this.productId = productId;\n        return this;\n    }\n\n    public Integer getAmount() {\n        return amount;\n    }\n\n    public ProductReduceStockDTO setAmount(Integer amount) {\n        this.amount = amount;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-product-service/src/main/java/cn/iocoder/springcloud/labx17/productservice/service/ProductService.java",
    "content": "package cn.iocoder.springcloud.labx17.productservice.service;\n\n/**\n * 商品 Service\n */\npublic interface ProductService {\n\n    /**\n     * 扣减库存\n     *\n     * @param productId 商品 ID\n     * @param amount    扣减数量\n     * @throws Exception 扣减失败时抛出异常\n     */\n    void reduceStock(Long productId, Integer amount) throws Exception;\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-product-service/src/main/java/cn/iocoder/springcloud/labx17/productservice/service/impl/ProductServiceImpl.java",
    "content": "package cn.iocoder.springcloud.labx17.productservice.service.impl;\n\nimport cn.iocoder.springcloud.labx17.productservice.dao.ProductDao;\nimport cn.iocoder.springcloud.labx17.productservice.service.ProductService;\nimport io.seata.core.context.RootContext;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\n\n@Service\npublic class ProductServiceImpl implements ProductService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private ProductDao productDao;\n\n    @Override\n    @Transactional // 开启新事物\n    public void reduceStock(Long productId, Integer amount) throws Exception {\n        logger.info(\"[reduceStock] 当前 XID: {}\", RootContext.getXID());\n\n        // 检查库存\n        checkStock(productId, amount);\n\n        logger.info(\"[reduceStock] 开始扣减 {} 库存\", productId);\n        // 扣减库存\n        int updateCount = productDao.reduceStock(productId, amount);\n        // 扣除成功\n        if (updateCount == 0) {\n            logger.warn(\"[reduceStock] 扣除 {} 库存失败\", productId);\n            throw new Exception(\"库存不足\");\n        }\n        // 扣除失败\n        logger.info(\"[reduceStock] 扣除 {} 库存成功\", productId);\n    }\n\n    private void checkStock(Long productId, Integer requiredAmount) throws Exception {\n        logger.info(\"[checkStock] 检查 {} 库存\", productId);\n        Integer stock = productDao.getStock(productId);\n        if (stock < requiredAmount) {\n            logger.warn(\"[checkStock] {} 库存不足，当前库存: {}\", productId, stock);\n            throw new Exception(\"库存不足\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-product-service/src/main/resources/application-file.yaml",
    "content": "server:\n  port: 8082\n\nspring:\n  application:\n    name: product-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      product-service-group: default\n    # 分组和 Seata 服务的映射\n    grouplist:\n      default: 127.0.0.1:8091\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/labx-17-sc-seata-at-feign-demo-product-service/src/main/resources/application.yaml",
    "content": "server:\n  port: 8082\n\nspring:\n  application:\n    name: product-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # Seata 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      product-service-group: default\n  # Seata 注册中心配置项，对应 RegistryProperties 类\n  registry:\n    type: nacos # 注册中心类型，默认为 file\n    nacos:\n      cluster: default # 使用的 Seata 分组\n      namespace: # Nacos 命名空间\n      serverAddr: localhost # Nacos 服务地址\n"
  },
  {
    "path": "labx-17/labx-17-sc-seata-at-feign-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-17</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-17-sc-seata-at-feign-demo</artifactId>\n\n    <modules>\n        <module>labx-17-sc-seata-at-feign-demo-account-service</module>\n        <module>labx-17-sc-seata-at-feign-demo-product-service</module>\n        <module>labx-17-sc-seata-at-feign-demo-order-service</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/data.sql",
    "content": "# Order\nDROP DATABASE IF EXISTS seata_order;\nCREATE DATABASE seata_order;\n\nCREATE TABLE seata_order.orders\n(\n    id               INT(11) NOT NULL AUTO_INCREMENT,\n    user_id          INT(11)        DEFAULT NULL,\n    product_id       INT(11)        DEFAULT NULL,\n    pay_amount       DECIMAL(10, 0) DEFAULT NULL,\n    add_time         DATETIME       DEFAULT CURRENT_TIMESTAMP,\n    last_update_time DATETIME       DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\n\nCREATE TABLE seata_order.undo_log\n(\n    id            BIGINT(20)   NOT NULL AUTO_INCREMENT,\n    branch_id     BIGINT(20)   NOT NULL,\n    xid           VARCHAR(100) NOT NULL,\n    context       VARCHAR(128) NOT NULL,\n    rollback_info LONGBLOB     NOT NULL,\n    log_status    INT(11)      NOT NULL,\n    log_created   DATETIME     NOT NULL,\n    log_modified  DATETIME     NOT NULL,\n    PRIMARY KEY (id),\n    UNIQUE KEY ux_undo_log (xid, branch_id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\n\n# Product\nDROP DATABASE IF EXISTS seata_product;\nCREATE DATABASE seata_product;\n\nCREATE TABLE seata_product.product\n(\n    id               INT(11) NOT NULL AUTO_INCREMENT,\n    stock            INT(11)  DEFAULT NULL,\n    last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\nINSERT INTO seata_product.product (id, stock) VALUES (1, 10); # 插入一条产品的库存\n\nCREATE TABLE seata_product.undo_log\n(\n    id            BIGINT(20)   NOT NULL AUTO_INCREMENT,\n    branch_id     BIGINT(20)   NOT NULL,\n    xid           VARCHAR(100) NOT NULL,\n    context       VARCHAR(128) NOT NULL,\n    rollback_info LONGBLOB     NOT NULL,\n    log_status    INT(11)      NOT NULL,\n    log_created   DATETIME     NOT NULL,\n    log_modified  DATETIME     NOT NULL,\n    PRIMARY KEY (id),\n    UNIQUE KEY ux_undo_log (xid, branch_id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\n\n# Account\nDROP DATABASE IF EXISTS seata_account;\nCREATE DATABASE seata_account;\n\nCREATE TABLE seata_account.account\n(\n    id               INT(11) NOT NULL AUTO_INCREMENT,\n    balance          DOUBLE   DEFAULT NULL,\n    last_update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n    PRIMARY KEY (id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1  DEFAULT CHARSET = utf8;\n\nCREATE TABLE seata_account.undo_log\n(\n    id            BIGINT(20)   NOT NULL AUTO_INCREMENT,\n    branch_id     BIGINT(20)   NOT NULL,\n    xid           VARCHAR(100) NOT NULL,\n    context       VARCHAR(128) NOT NULL,\n    rollback_info LONGBLOB     NOT NULL,\n    log_status    INT(11)      NOT NULL,\n    log_created   DATETIME     NOT NULL,\n    log_modified  DATETIME     NOT NULL,\n    PRIMARY KEY (id),\n    UNIQUE KEY ux_undo_log (xid, branch_id)\n) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;\nINSERT INTO seata_account.account (id, balance) VALUES (1, 10);\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-account-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-17-sca-seata-at-dubbo-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-17-sca-seata-at-dubbo-demo-account-service</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-17-sca-seata-at-dubbo-demo-account-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- Spring Boot Starter 基础库 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>druid-spring-boot-starter</artifactId>\n            <version>1.1.10</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.2</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Seata 相关依赖，使用 Seata 实现分布式事务，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-alibaba-seata</artifactId>\n        </dependency>\n        <dependency> <!-- 主要想使用 seata 1.1.0 版本 -->\n            <groupId>io.seata</groupId>\n            <artifactId>seata-spring-boot-starter</artifactId>\n            <version>1.1.0</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-account-service/src/main/java/cn/iocoder/springcloudalibaba/labx17/accountservice/AccountServiceApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx17.accountservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class AccountServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(AccountServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-account-service/src/main/java/cn/iocoder/springcloudalibaba/labx17/accountservice/dao/AccountDao.java",
    "content": "package cn.iocoder.springcloudalibaba.labx17.accountservice.dao;\n\n\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\nimport org.apache.ibatis.annotations.Update;\nimport org.springframework.stereotype.Repository;\n\n@Mapper\n@Repository\npublic interface AccountDao {\n\n    /**\n     * 获取账户余额\n     *\n     * @param userId 用户 ID\n     * @return 账户余额\n     */\n    @Select(\"SELECT balance FROM account WHERE id = #{userId}\")\n    Integer getBalance(@Param(\"userId\") Long userId);\n\n    /**\n     * 扣减余额\n     *\n     * @param price 需要扣减的数目\n     * @return 影响记录行数\n     */\n    @Update(\"UPDATE account SET balance = balance - #{price} WHERE id = 1 AND balance >= ${price}\")\n    int reduceBalance(@Param(\"price\") Integer price);\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-account-service/src/main/java/cn/iocoder/springcloudalibaba/labx17/accountservice/service/AccountServiceImpl.java",
    "content": "package cn.iocoder.springcloudalibaba.labx17.accountservice.service;\n\nimport cn.iocoder.springcloudalibaba.labx17.accountservice.api.AccountService;\nimport cn.iocoder.springcloudalibaba.labx17.accountservice.dao.AccountDao;\nimport io.seata.core.context.RootContext;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.transaction.annotation.Transactional;\n\n@org.apache.dubbo.config.annotation.Service\npublic class AccountServiceImpl implements AccountService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private AccountDao accountDao;\n\n    @Override\n    @Transactional // 开启新事物\n    public void reduceBalance(Long userId, Integer price) throws Exception {\n        logger.info(\"[reduceBalance] 当前 XID: {}\", RootContext.getXID());\n\n        // 检查余额\n        checkBalance(userId, price);\n\n        logger.info(\"[reduceBalance] 开始扣减用户 {} 余额\", userId);\n        // 扣除余额\n        int updateCount = accountDao.reduceBalance(price);\n        // 扣除成功\n        if (updateCount == 0) {\n            logger.warn(\"[reduceBalance] 扣除用户 {} 余额失败\", userId);\n            throw new Exception(\"余额不足\");\n        }\n        logger.info(\"[reduceBalance] 扣除用户 {} 余额成功\", userId);\n    }\n\n    private void checkBalance(Long userId, Integer price) throws Exception {\n        logger.info(\"[checkBalance] 检查用户 {} 余额\", userId);\n        Integer balance = accountDao.getBalance(userId);\n        if (balance < price) {\n            logger.warn(\"[checkBalance] 用户 {} 余额不足，当前余额:{}\", userId, balance);\n            throw new Exception(\"余额不足\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-account-service/src/main/resources/application-file.yaml",
    "content": "spring:\n  application:\n    name: account-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springcloudalibaba.labx17.accountservice.service\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: '' # 设置订阅的应用列表，默认为 * 订阅所有应用。\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      account-service-group: default\n    # 分组和 Seata 服务的映射\n    grouplist:\n      default: 127.0.0.1:8091\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-account-service/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: account-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_account?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springcloudalibaba.labx17.accountservice.service\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: '' # 设置订阅的应用列表，默认为 * 订阅所有应用。\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # Seata 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      account-service-group: default\n  # Seata 注册中心配置项，对应 RegistryProperties 类\n  registry:\n    type: nacos # 注册中心类型，默认为 file\n    nacos:\n      cluster: default # 使用的 Seata 分组\n      namespace: # Nacos 命名空间\n      serverAddr: localhost # Nacos 服务地址\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-account-service-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-17-sca-seata-at-dubbo-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-17-sca-seata-at-dubbo-demo-account-service-api</artifactId>\n\n</project>\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-account-service-api/src/main/java/cn/iocoder/springcloudalibaba/labx17/accountservice/api/AccountService.java",
    "content": "package cn.iocoder.springcloudalibaba.labx17.accountservice.api;\n\n/**\n * 账户 Service\n */\npublic interface AccountService {\n\n    /**\n     * 扣除余额\n     *\n     * @param userId 用户编号\n     * @param price  扣减金额\n     * @throws Exception 失败时抛出异常\n     */\n    void reduceBalance(Long userId, Integer price) throws Exception;\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-order-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-17-sca-seata-at-dubbo-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-17-sca-seata-at-dubbo-demo-order-service</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-17-sca-seata-at-dubbo-demo-order-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-17-sca-seata-at-dubbo-demo-account-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-17-sca-seata-at-dubbo-demo-product-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 实现对 Spring MVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>druid-spring-boot-starter</artifactId>\n            <version>1.1.10</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.2</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Seata 相关依赖，使用 Seata 实现分布式事务，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-alibaba-seata</artifactId>\n        </dependency>\n        <dependency> <!-- 主要想使用 seata 1.1.0 版本 -->\n            <groupId>io.seata</groupId>\n            <artifactId>seata-spring-boot-starter</artifactId>\n            <version>1.1.0</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-order-service/src/main/java/cn/iocoder/springcloudalibaba/labx17/orderservice/OrderServiceApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx17.orderservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class OrderServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(OrderServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-order-service/src/main/java/cn/iocoder/springcloudalibaba/labx17/orderservice/controller/OrderController.java",
    "content": "package cn.iocoder.springcloudalibaba.labx17.orderservice.controller;\n\nimport cn.iocoder.springcloudalibaba.labx17.orderservice.api.OrderService;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/order\")\npublic class OrderController {\n\n    private Logger logger = LoggerFactory.getLogger(OrderController.class);\n\n    @Reference\n    private OrderService orderService;\n\n    @PostMapping(\"/create\")\n    public Integer createOrder(@RequestParam(\"userId\") Long userId,\n                               @RequestParam(\"productId\") Long productId,\n                               @RequestParam(\"price\") Integer price) throws Exception {\n        logger.info(\"[createOrder] 收到下单请求,用户:{}, 商品:{}, 价格:{}\", userId, productId, price);\n        return orderService.createOrder(userId, productId, price);\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-order-service/src/main/java/cn/iocoder/springcloudalibaba/labx17/orderservice/dao/OrderDao.java",
    "content": "package cn.iocoder.springcloudalibaba.labx17.orderservice.dao;\n\nimport cn.iocoder.springcloudalibaba.labx17.orderservice.entity.OrderDO;\nimport org.apache.ibatis.annotations.Insert;\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Options;\nimport org.springframework.stereotype.Repository;\n\n@Mapper\n@Repository\npublic interface OrderDao {\n\n    /**\n     * 插入订单记录\n     *\n     * @param order 订单\n     * @return 影响记录数量\n     */\n    @Insert(\"INSERT INTO orders (user_id, product_id, pay_amount) VALUES (#{userId}, #{productId}, #{payAmount})\")\n    @Options(useGeneratedKeys = true, keyColumn = \"id\", keyProperty = \"id\")\n    int saveOrder(OrderDO order);\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-order-service/src/main/java/cn/iocoder/springcloudalibaba/labx17/orderservice/entity/OrderDO.java",
    "content": "package cn.iocoder.springcloudalibaba.labx17.orderservice.entity;\n\n/**\n * 订单实体\n */\npublic class OrderDO {\n\n    /** 订单编号 **/\n    private Integer id;\n\n    /** 用户编号 **/\n    private Long userId;\n\n    /** 产品编号 **/\n    private Long productId;\n\n    /** 支付金额 **/\n    private Integer payAmount;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public OrderDO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public Long getUserId() {\n        return userId;\n    }\n\n    public OrderDO setUserId(Long userId) {\n        this.userId = userId;\n        return this;\n    }\n\n    public Long getProductId() {\n        return productId;\n    }\n\n    public OrderDO setProductId(Long productId) {\n        this.productId = productId;\n        return this;\n    }\n\n    public Integer getPayAmount() {\n        return payAmount;\n    }\n\n    public OrderDO setPayAmount(Integer payAmount) {\n        this.payAmount = payAmount;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-order-service/src/main/java/cn/iocoder/springcloudalibaba/labx17/orderservice/service/OrderServiceImpl.java",
    "content": "package cn.iocoder.springcloudalibaba.labx17.orderservice.service;\n\nimport cn.iocoder.springcloudalibaba.labx17.accountservice.api.AccountService;\nimport cn.iocoder.springcloudalibaba.labx17.orderservice.api.OrderService;\nimport cn.iocoder.springcloudalibaba.labx17.orderservice.dao.OrderDao;\nimport cn.iocoder.springcloudalibaba.labx17.orderservice.entity.OrderDO;\nimport cn.iocoder.springcloudalibaba.labx17.producctservice.api.ProductService;\nimport io.seata.core.context.RootContext;\nimport io.seata.spring.annotation.GlobalTransactional;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\n\n@org.apache.dubbo.config.annotation.Service\npublic class OrderServiceImpl implements OrderService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private OrderDao orderDao;\n\n    @Reference\n    private AccountService accountService;\n    @Reference\n    private ProductService productService;\n\n    @Override\n    @GlobalTransactional\n    public Integer createOrder(Long userId, Long productId, Integer price) throws Exception {\n        Integer amount = 1; // 购买数量，暂时设置为 1。\n\n        logger.info(\"[createOrder] 当前 XID: {}\", RootContext.getXID());\n\n        // 扣减库存\n        productService.reduceStock(productId, amount);\n\n        // 扣减余额\n        accountService.reduceBalance(userId, price);\n\n        // 保存订单\n        OrderDO order = new OrderDO().setUserId(userId).setProductId(productId).setPayAmount(amount * price);\n        orderDao.saveOrder(order);\n        logger.info(\"[createOrder] 保存订单: {}\", order.getId());\n\n        // 返回订单编号\n        return order.getId();\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-order-service/src/main/resources/application-file.yaml",
    "content": "server:\n  port: 8081 # 端口\n\nspring:\n  application:\n    name: order-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springcloudalibaba.labx17.orderservice.service\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: account-service, product-service # 设置订阅的应用列表，默认为 * 订阅所有应用。\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      order-service-group: default\n    # 分组和 Seata 服务的映射\n    grouplist:\n      default: 127.0.0.1:8091\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-order-service/src/main/resources/application.yaml",
    "content": "server:\n  port: 8081 # 端口\n\nspring:\n  application:\n    name: order-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springcloudalibaba.labx17.orderservice.service\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: account-service, product-service # 设置订阅的应用列表，默认为 * 订阅所有应用。\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # Seata 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      order-service-group: default\n  # Seata 注册中心配置项，对应 RegistryProperties 类\n  registry:\n    type: nacos # 注册中心类型，默认为 file\n    nacos:\n      cluster: default # 使用的 Seata 分组\n      namespace: # Nacos 命名空间\n      serverAddr: localhost # Nacos 服务地址\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-order-service-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-17-sca-seata-at-dubbo-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-17-sca-seata-at-dubbo-demo-order-service-api</artifactId>\n\n</project>\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-order-service-api/src/main/java/cn/iocoder/springcloudalibaba/labx17/orderservice/api/OrderService.java",
    "content": "package cn.iocoder.springcloudalibaba.labx17.orderservice.api;\n\n/**\n * 订单 Service\n */\npublic interface OrderService {\n\n    /**\n     * 创建订单\n     *\n     * @param userId 用户编号\n     * @param productId 产品编号\n     * @param price 价格\n     * @return 订单编号\n     * @throws Exception 创建订单失败，抛出异常\n     */\n    Integer createOrder(Long userId, Long productId, Integer price) throws Exception;\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-product-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-17-sca-seata-at-dubbo-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-17-sca-seata-at-dubbo-demo-product-service</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-17-sca-seata-at-dubbo-demo-product-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- Spring Boot Starter 基础库 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter</artifactId>\n        </dependency>\n\n        <!-- 实现对数据库连接池的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-jdbc</artifactId>\n        </dependency>\n        <dependency> <!-- 本示例，我们使用 MySQL -->\n            <groupId>mysql</groupId>\n            <artifactId>mysql-connector-java</artifactId>\n            <version>5.1.48</version>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba</groupId>\n            <artifactId>druid-spring-boot-starter</artifactId>\n            <version>1.1.10</version>\n        </dependency>\n\n        <!-- 实现对 MyBatis 的自动化配置 -->\n        <dependency>\n            <groupId>org.mybatis.spring.boot</groupId>\n            <artifactId>mybatis-spring-boot-starter</artifactId>\n            <version>2.1.2</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Seata 相关依赖，使用 Seata 实现分布式事务，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-alibaba-seata</artifactId>\n        </dependency>\n        <dependency> <!-- 主要想使用 seata 1.1.0 版本 -->\n            <groupId>io.seata</groupId>\n            <artifactId>seata-spring-boot-starter</artifactId>\n            <version>1.1.0</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-product-service/src/main/java/cn/iocoder/springcloudalibaba/labx17/productservice/ProductServiceApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx17.productservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class ProductServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ProductServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-product-service/src/main/java/cn/iocoder/springcloudalibaba/labx17/productservice/dao/ProductDao.java",
    "content": "package cn.iocoder.springcloudalibaba.labx17.productservice.dao;\n\n\nimport org.apache.ibatis.annotations.Mapper;\nimport org.apache.ibatis.annotations.Param;\nimport org.apache.ibatis.annotations.Select;\nimport org.apache.ibatis.annotations.Update;\nimport org.springframework.stereotype.Repository;\n\n@Mapper\n@Repository\npublic interface ProductDao {\n\n    /**\n     * 获取库存\n     *\n     * @param productId 商品编号\n     * @return 库存\n     */\n    @Select(\"SELECT stock FROM product WHERE id = #{productId}\")\n    Integer getStock(@Param(\"productId\") Long productId);\n\n    /**\n     * 扣减库存\n     *\n     * @param productId 商品编号\n     * @param amount    扣减数量\n     * @return 影响记录行数\n     */\n    @Update(\"UPDATE product SET stock = stock - #{amount} WHERE id = #{productId} AND stock >= #{amount}\")\n    int reduceStock(@Param(\"productId\") Long productId, @Param(\"amount\") Integer amount);\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-product-service/src/main/java/cn/iocoder/springcloudalibaba/labx17/productservice/service/ProductServiceImpl.java",
    "content": "package cn.iocoder.springcloudalibaba.labx17.productservice.service;\n\nimport cn.iocoder.springcloudalibaba.labx17.producctservice.api.ProductService;\nimport cn.iocoder.springcloudalibaba.labx17.productservice.dao.ProductDao;\nimport io.seata.core.context.RootContext;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.transaction.annotation.Transactional;\n\n@org.apache.dubbo.config.annotation.Service\npublic class ProductServiceImpl implements ProductService {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private ProductDao productDao;\n\n    @Override\n    @Transactional // 开启新事物\n    public void reduceStock(Long productId, Integer amount) throws Exception {\n        logger.info(\"[reduceStock] 当前 XID: {}\", RootContext.getXID());\n\n        // 检查库存\n        checkStock(productId, amount);\n\n        logger.info(\"[reduceStock] 开始扣减 {} 库存\", productId);\n        // 扣减库存\n        int updateCount = productDao.reduceStock(productId, amount);\n        // 扣除成功\n        if (updateCount == 0) {\n            logger.warn(\"[reduceStock] 扣除 {} 库存失败\", productId);\n            throw new Exception(\"库存不足\");\n        }\n        // 扣除失败\n        logger.info(\"[reduceStock] 扣除 {} 库存成功\", productId);\n    }\n\n    private void checkStock(Long productId, Integer requiredAmount) throws Exception {\n        logger.info(\"[checkStock] 检查 {} 库存\", productId);\n        Integer stock = productDao.getStock(productId);\n        if (stock < requiredAmount) {\n            logger.warn(\"[checkStock] {} 库存不足，当前库存: {}\", productId, stock);\n            throw new Exception(\"库存不足\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-product-service/src/main/resources/application-file.yaml",
    "content": "spring:\n  application:\n    name: product-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springcloudalibaba.labx17.productservice.service\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: '' # 设置订阅的应用列表，默认为 * 订阅所有应用。\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      product-service-group: default\n    # 分组和 Seata 服务的映射\n    grouplist:\n      default: 127.0.0.1:8091\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-product-service/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: product-service\n\n  datasource:\n    url: jdbc:mysql://127.0.0.1:3306/seata_product?useSSL=false&useUnicode=true&characterEncoding=UTF-8\n    driver-class-name: com.mysql.jdbc.Driver\n    username: root\n    password:\n\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n# dubbo 配置项，对应 DubboConfigurationProperties 配置类\ndubbo:\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Dubbo 服务提供者协议配置\n  protocol:\n    port: -1 # 协议端口。使用 -1 表示随机端口。\n    name: dubbo # 使用 `dubbo://` 协议。更多协议，可见 http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html 文档\n  # 配置扫描 Dubbo 自定义的 @Service 注解，暴露成 Dubbo 服务提供者\n  scan:\n    base-packages: cn.iocoder.springcloudalibaba.labx17.productservice.service\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: '' # 设置订阅的应用列表，默认为 * 订阅所有应用。\n\n# Seata 配置项，对应 SeataProperties 类\nseata:\n  application-id: ${spring.application.name} # Seata 应用编号，默认为 ${spring.application.name}\n  tx-service-group: ${spring.application.name}-group # Seata 事务组编号，用于 TC 集群名\n  # Seata 服务配置项，对应 ServiceProperties 类\n  service:\n    # 虚拟组和分组的映射\n    vgroup-mapping:\n      product-service-group: default\n  # Seata 注册中心配置项，对应 RegistryProperties 类\n  registry:\n    type: nacos # 注册中心类型，默认为 file\n    nacos:\n      cluster: default # 使用的 Seata 分组\n      namespace: # Nacos 命名空间\n      serverAddr: localhost # Nacos 服务地址\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-product-service-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-17-sca-seata-at-dubbo-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-17-sca-seata-at-dubbo-demo-product-service-api</artifactId>\n\n</project>\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/labx-17-sca-seata-at-dubbo-demo-product-service-api/src/main/java/cn/iocoder/springcloudalibaba/labx17/producctservice/api/ProductService.java",
    "content": "package cn.iocoder.springcloudalibaba.labx17.producctservice.api;\n\n/**\n * 商品 Service\n */\npublic interface ProductService {\n\n    /**\n     * 扣减库存\n     *\n     * @param productId 商品 ID\n     * @param amount    扣减数量\n     * @throws Exception 扣减失败时抛出异常\n     */\n    void reduceStock(Long productId, Integer amount) throws Exception;\n\n}\n"
  },
  {
    "path": "labx-17/labx-17-sca-seata-at-dubbo-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-17</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-17-sca-seata-at-dubbo-demo</artifactId>\n\n    <modules>\n        <module>labx-17-sca-seata-at-dubbo-demo-account-service-api</module>\n        <module>labx-17-sca-seata-at-dubbo-demo-account-service</module>\n        <module>labx-17-sca-seata-at-dubbo-demo-product-service-api</module>\n        <module>labx-17-sca-seata-at-dubbo-demo-product-service</module>\n        <module>labx-17-sca-seata-at-dubbo-demo-order-service-api</module>\n        <module>labx-17-sca-seata-at-dubbo-demo-order-service</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-17/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-17</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-17-sca-seata-at-dubbo-demo</module>\n        <module>labx-17-sc-seata-at-feign-demo</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-17/《芋道 Spring Cloud Alibaba 分布式事务 Seata 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud-Alibaba/Seata/?github>\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-listener/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-18</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-18-sc-bus-rabbitmq-demo-listener</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入基于 RabbitMQ 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-amqp</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-listener/src/main/java/cn/iocoder/springcloud/labx18/listenerdemo/ListenerDemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx18.listenerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.bus.jackson.RemoteApplicationEventScan;\n\n@SpringBootApplication\n@RemoteApplicationEventScan\npublic class ListenerDemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ListenerDemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-listener/src/main/java/cn/iocoder/springcloud/labx18/listenerdemo/event/UserRegisterEvent.java",
    "content": "package cn.iocoder.springcloud.labx18.listenerdemo.event;\n\nimport org.springframework.cloud.bus.event.RemoteApplicationEvent;\n\n/**\n * 用户注册事件\n */\npublic class UserRegisterEvent extends RemoteApplicationEvent {\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    public UserRegisterEvent() { // 序列化\n    }\n\n    public UserRegisterEvent(Object source, String originService, String destinationService, String username) {\n        super(source, originService);\n        this.username = username;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n}\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-listener/src/main/java/cn/iocoder/springcloud/labx18/listenerdemo/listener/UserRegisterListener.java",
    "content": "package cn.iocoder.springcloud.labx18.listenerdemo.listener;\n\nimport cn.iocoder.springcloud.labx18.listenerdemo.event.UserRegisterEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.stereotype.Component;\n\n/**\n * 用户注册事件的监听器\n */\n@Component\npublic class UserRegisterListener implements ApplicationListener<UserRegisterEvent> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void onApplicationEvent(UserRegisterEvent event) {\n        logger.info(\"[onApplicationEvent][监听到用户({}) 注册]\", event.getUsername());\n    }\n\n}\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-listener/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: listener-demo\n\n  # RabbitMQ 相关配置项\n  rabbitmq:\n    host: localhost\n    port: 5672\n    username: guest\n    password: guest\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-listener-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-18</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-18-sc-bus-rabbitmq-demo-listener-actuator</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入基于 RabbitMQ 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-amqp</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-listener-actuator/src/main/java/cn/iocoder/springcloud/labx18/listenerdemo/ListenerDemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx18.listenerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.bus.jackson.RemoteApplicationEventScan;\n\n@SpringBootApplication\n@RemoteApplicationEventScan\npublic class ListenerDemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ListenerDemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-listener-actuator/src/main/java/cn/iocoder/springcloud/labx18/listenerdemo/event/UserRegisterEvent.java",
    "content": "package cn.iocoder.springcloud.labx18.listenerdemo.event;\n\nimport org.springframework.cloud.bus.event.RemoteApplicationEvent;\n\n/**\n * 用户注册事件\n */\npublic class UserRegisterEvent extends RemoteApplicationEvent {\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    public UserRegisterEvent() { // 序列化\n    }\n\n    public UserRegisterEvent(Object source, String originService, String destinationService, String username) {\n        super(source, originService);\n        this.username = username;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n}\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-listener-actuator/src/main/java/cn/iocoder/springcloud/labx18/listenerdemo/listener/UserRegisterListener.java",
    "content": "package cn.iocoder.springcloud.labx18.listenerdemo.listener;\n\nimport cn.iocoder.springcloud.labx18.listenerdemo.event.UserRegisterEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.stereotype.Component;\n\n/**\n * 用户注册事件的监听器\n */\n@Component\npublic class UserRegisterListener implements ApplicationListener<UserRegisterEvent> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void onApplicationEvent(UserRegisterEvent event) {\n        logger.info(\"[onApplicationEvent][监听到用户({}) 注册]\", event.getUsername());\n    }\n\n}\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-listener-actuator/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: listener-demo\n\n  # RabbitMQ 相关配置项\n  rabbitmq:\n    host: localhost\n    port: 5672\n    username: guest\n    password: guest\n\nserver:\n  port: 18080 # 随机端口，方便启动多个消费者\n\nmanagement:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-publisher/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-18</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-18-sc-bus-rabbitmq-demo-publisher</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入基于 RabbitMQ 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-amqp</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-publisher/src/main/java/cn/iocoder/springcloud/labx18/publisherdemo/PublisherDemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx18.publisherdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n//@RemoteApplicationEventScan\npublic class PublisherDemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(PublisherDemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-publisher/src/main/java/cn/iocoder/springcloud/labx18/publisherdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx18.publisherdemo.controller;\n\nimport cn.iocoder.springcloud.labx18.publisherdemo.event.UserRegisterEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.bus.ServiceMatcher;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private ApplicationEventPublisher applicationEventPublisher;\n\n    @Autowired\n    private ServiceMatcher busServiceMatcher;\n\n    @GetMapping(\"/register\")\n    public String register(String username) {\n        // ... 执行注册逻辑\n        logger.info(\"[register][执行用户({}) 的注册逻辑]\", username);\n\n        // ... 发布\n        applicationEventPublisher.publishEvent(new UserRegisterEvent(this, busServiceMatcher.getServiceId(),\n                null, username));\n        return \"success\";\n    }\n\n\n}\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-publisher/src/main/java/cn/iocoder/springcloud/labx18/publisherdemo/event/UserRegisterEvent.java",
    "content": "package cn.iocoder.springcloud.labx18.publisherdemo.event;\n\nimport org.springframework.cloud.bus.event.RemoteApplicationEvent;\n\n/**\n * 用户注册事件\n */\npublic class UserRegisterEvent extends RemoteApplicationEvent {\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    public UserRegisterEvent() { // 序列化\n    }\n\n    public UserRegisterEvent(Object source, String originService, String destinationService, String username) {\n        super(source, originService);\n        this.username = username;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n}\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-publisher/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: publisher-demo\n\n  # RabbitMQ 相关配置项\n  rabbitmq:\n    host: localhost\n    port: 5672\n    username: guest\n    password: guest\n\n  # Bus 相关配置项，对应 BusProperties\n  cloud:\n    bus:\n      enabled: true # 是否开启，默认为 true\n      destination: springCloudBus # 目标消息队列，默认为 springCloudBus\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-publisher-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-18</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-18-sc-bus-rabbitmq-demo-publisher-actuator</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入基于 RabbitMQ 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-amqp</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-publisher-actuator/src/main/java/cn/iocoder/springcloud/labx18/publisherdemo/PublisherDemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx18.publisherdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n//@RemoteApplicationEventScan\npublic class PublisherDemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(PublisherDemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-publisher-actuator/src/main/java/cn/iocoder/springcloud/labx18/publisherdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx18.publisherdemo.controller;\n\nimport cn.iocoder.springcloud.labx18.publisherdemo.event.UserRegisterEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.bus.ServiceMatcher;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private ApplicationEventPublisher applicationEventPublisher;\n\n    @Autowired\n    private ServiceMatcher busServiceMatcher;\n\n    @GetMapping(\"/register\")\n    public String register(String username) {\n        // ... 执行注册逻辑\n        logger.info(\"[register][执行用户({}) 的注册逻辑]\", username);\n\n        // ... 发布\n        applicationEventPublisher.publishEvent(new UserRegisterEvent(this, busServiceMatcher.getServiceId(),\n                null, username));\n        return \"success\";\n    }\n\n\n}\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-publisher-actuator/src/main/java/cn/iocoder/springcloud/labx18/publisherdemo/event/UserRegisterEvent.java",
    "content": "package cn.iocoder.springcloud.labx18.publisherdemo.event;\n\nimport org.springframework.cloud.bus.event.RemoteApplicationEvent;\n\n/**\n * 用户注册事件\n */\npublic class UserRegisterEvent extends RemoteApplicationEvent {\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    public UserRegisterEvent() { // 序列化\n    }\n\n    public UserRegisterEvent(Object source, String originService, String destinationService, String username) {\n        super(source, originService);\n        this.username = username;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n}\n"
  },
  {
    "path": "labx-18/labx-18-sc-bus-rabbitmq-demo-publisher-actuator/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: publisher-demo\n\n  # RabbitMQ 相关配置项\n  rabbitmq:\n    host: localhost\n    port: 5672\n    username: guest\n    password: guest\n\nmanagement:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n"
  },
  {
    "path": "labx-18/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-18</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-18-sc-bus-rabbitmq-demo-publisher</module>\n        <module>labx-18-sc-bus-rabbitmq-demo-listener</module>\n\n        <module>labx-18-sc-bus-rabbitmq-demo-listener-actuator</module>\n        <module>labx-18-sc-bus-rabbitmq-demo-publisher-actuator</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-18/《芋道 Spring Cloud 事件总线 RabbitMQ 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/Bus-RabbitMQ/?github>\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-listener/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-19</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-19-sc-bus-kafka-demo-listener</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入基于 Kafka 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-listener/src/main/java/cn/iocoder/springcloud/labx19/listenerdemo/ListenerDemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx19.listenerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.bus.jackson.RemoteApplicationEventScan;\n\n@SpringBootApplication\n@RemoteApplicationEventScan\npublic class ListenerDemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ListenerDemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-listener/src/main/java/cn/iocoder/springcloud/labx19/listenerdemo/event/UserRegisterEvent.java",
    "content": "package cn.iocoder.springcloud.labx19.listenerdemo.event;\n\nimport org.springframework.cloud.bus.event.RemoteApplicationEvent;\n\n/**\n * 用户注册事件\n */\npublic class UserRegisterEvent extends RemoteApplicationEvent {\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    public UserRegisterEvent() { // 序列化\n    }\n\n    public UserRegisterEvent(Object source, String originService, String destinationService, String username) {\n        super(source, originService);\n        this.username = username;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n}\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-listener/src/main/java/cn/iocoder/springcloud/labx19/listenerdemo/listener/UserRegisterListener.java",
    "content": "package cn.iocoder.springcloud.labx19.listenerdemo.listener;\n\nimport cn.iocoder.springcloud.labx19.listenerdemo.event.UserRegisterEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.stereotype.Component;\n\n/**\n * 用户注册事件的监听器\n */\n@Component\npublic class UserRegisterListener implements ApplicationListener<UserRegisterEvent> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void onApplicationEvent(UserRegisterEvent event) {\n        logger.info(\"[onApplicationEvent][监听到用户({}) 注册]\", event.getUsername());\n    }\n\n}\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-listener/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: listener-demo\n\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-listener-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-19</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-19-sc-bus-kafka-demo-listener-actuator</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入基于 Kafka 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-kafka</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-listener-actuator/src/main/java/cn/iocoder/springcloud/labx19/listenerdemo/ListenerDemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx19.listenerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.bus.jackson.RemoteApplicationEventScan;\n\n@SpringBootApplication\n@RemoteApplicationEventScan\npublic class ListenerDemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ListenerDemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-listener-actuator/src/main/java/cn/iocoder/springcloud/labx19/listenerdemo/event/UserRegisterEvent.java",
    "content": "package cn.iocoder.springcloud.labx19.listenerdemo.event;\n\nimport org.springframework.cloud.bus.event.RemoteApplicationEvent;\n\n/**\n * 用户注册事件\n */\npublic class UserRegisterEvent extends RemoteApplicationEvent {\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    public UserRegisterEvent() { // 序列化\n    }\n\n    public UserRegisterEvent(Object source, String originService, String destinationService, String username) {\n        super(source, originService);\n        this.username = username;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n}\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-listener-actuator/src/main/java/cn/iocoder/springcloud/labx19/listenerdemo/listener/UserRegisterListener.java",
    "content": "package cn.iocoder.springcloud.labx19.listenerdemo.listener;\n\nimport cn.iocoder.springcloud.labx19.listenerdemo.event.UserRegisterEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.stereotype.Component;\n\n/**\n * 用户注册事件的监听器\n */\n@Component\npublic class UserRegisterListener implements ApplicationListener<UserRegisterEvent> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void onApplicationEvent(UserRegisterEvent event) {\n        logger.info(\"[onApplicationEvent][监听到用户({}) 注册]\", event.getUsername());\n    }\n\n}\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-listener-actuator/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: listener-demo\n\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n\nserver:\n  port: 18080 # 随机端口，方便启动多个消费者\n\nmanagement:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-publisher/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-19</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-19-sc-bus-kafka-demo-publisher</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入基于 Kafka 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-publisher/src/main/java/cn/iocoder/springcloud/labx19/publisherdemo/PublisherDemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx19.publisherdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n//@RemoteApplicationEventScan\npublic class PublisherDemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(PublisherDemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-publisher/src/main/java/cn/iocoder/springcloud/labx19/publisherdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx19.publisherdemo.controller;\n\nimport cn.iocoder.springcloud.labx19.publisherdemo.event.UserRegisterEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.bus.ServiceMatcher;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private ApplicationEventPublisher applicationEventPublisher;\n\n    @Autowired\n    private ServiceMatcher busServiceMatcher;\n\n    @GetMapping(\"/register\")\n    public String register(String username) {\n        // ... 执行注册逻辑\n        logger.info(\"[register][执行用户({}) 的注册逻辑]\", username);\n\n        // ... 发布\n        applicationEventPublisher.publishEvent(new UserRegisterEvent(this, busServiceMatcher.getServiceId(),\n                null, username));\n        return \"success\";\n    }\n\n}\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-publisher/src/main/java/cn/iocoder/springcloud/labx19/publisherdemo/event/UserRegisterEvent.java",
    "content": "package cn.iocoder.springcloud.labx19.publisherdemo.event;\n\nimport org.springframework.cloud.bus.event.RemoteApplicationEvent;\n\n/**\n * 用户注册事件\n */\npublic class UserRegisterEvent extends RemoteApplicationEvent {\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    public UserRegisterEvent() { // 序列化\n    }\n\n    public UserRegisterEvent(Object source, String originService, String destinationService, String username) {\n        super(source, originService);\n        this.username = username;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n}\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-publisher/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: publisher-demo\n\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n\n  # Bus 相关配置项，对应 BusProperties\n  cloud:\n    bus:\n      enabled: true # 是否开启，默认为 true\n      destination: springCloudBus # 目标消息队列，默认为 springCloudBus\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-publisher-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-19</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-19-sc-bus-kafka-demo-publisher-actuator</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入基于 Kafka 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-kafka</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-publisher-actuator/src/main/java/cn/iocoder/springcloud/labx19/publisherdemo/PublisherDemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx19.publisherdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n//@RemoteApplicationEventScan\npublic class PublisherDemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(PublisherDemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-publisher-actuator/src/main/java/cn/iocoder/springcloud/labx19/publisherdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx19.publisherdemo.controller;\n\nimport cn.iocoder.springcloud.labx19.publisherdemo.event.UserRegisterEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.bus.ServiceMatcher;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private ApplicationEventPublisher applicationEventPublisher;\n\n    @Autowired\n    private ServiceMatcher busServiceMatcher;\n\n    @GetMapping(\"/register\")\n    public String register(String username) {\n        // ... 执行注册逻辑\n        logger.info(\"[register][执行用户({}) 的注册逻辑]\", username);\n\n        // ... 发布\n        applicationEventPublisher.publishEvent(new UserRegisterEvent(this, busServiceMatcher.getServiceId(),\n                null, username));\n        return \"success\";\n    }\n\n\n}\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-publisher-actuator/src/main/java/cn/iocoder/springcloud/labx19/publisherdemo/event/UserRegisterEvent.java",
    "content": "package cn.iocoder.springcloud.labx19.publisherdemo.event;\n\nimport org.springframework.cloud.bus.event.RemoteApplicationEvent;\n\n/**\n * 用户注册事件\n */\npublic class UserRegisterEvent extends RemoteApplicationEvent {\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    public UserRegisterEvent() { // 序列化\n    }\n\n    public UserRegisterEvent(Object source, String originService, String destinationService, String username) {\n        super(source, originService);\n        this.username = username;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n}\n"
  },
  {
    "path": "labx-19/labx-19-sc-bus-kafka-demo-publisher-actuator/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: publisher-demo\n\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n\nmanagement:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n"
  },
  {
    "path": "labx-19/labx-19-sc-config-server-git-auto-refresh-by-bus/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-12</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-19-sc-config-server-git-auto-refresh-by-bus</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-config-server</artifactId>\n        </dependency>\n\n        <!-- 引入基于 Kafka 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-kafka</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Config Monitor 依赖，提供 `/monitor` 接口，用于刷新配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-config-monitor</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-19/labx-19-sc-config-server-git-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx19/configserverdemo/ConfigServerApplication.java",
    "content": "package cn.iocoder.springcloud.labx19.configserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.config.server.EnableConfigServer;\n\n@SpringBootApplication\n@EnableConfigServer\npublic class ConfigServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConfigServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-19/labx-19-sc-config-server-git-auto-refresh-by-bus/src/main/resources/application.yml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: demo-config-server\n\n  profiles:\n    active: git # 使用的 Spring Cloud Config Server 的存储器方案\n  # Spring Cloud Config 相关配置项\n  cloud:\n    config:\n      server:\n        # Spring Cloud Config Server 的 Git 存储器的配置项，对应 MultipleJGitEnvironmentProperties 类\n        git:\n          uri: https://github.com/YunaiV/demo-config-server.git # Git 仓库地址\n          search-paths: / # 读取文件的根地址\n          default-label: master # 使用的默认分支，默认为 master\n  #          username: ${CODING_USERNAME} # 账号\n  #          password: ${CODING_PASSWORD} # 密码\n\n  # Kafka 配置项，对应 KafkaProperties 配置类\n  kafka:\n    bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n"
  },
  {
    "path": "labx-19/labx-19-sc-config-user-application-auto-refresh-by-bus/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-12</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-19-sc-config-user-application-auto-refresh-by-bus</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Config Client 相关依赖，作为配置中心的客户端，并实现自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-config</artifactId>\n        </dependency>\n\n        <!-- 引入基于 Kafka 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-kafka</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-19/labx-19-sc-config-user-application-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx19/userapplication/UserApplication.java",
    "content": "package cn.iocoder.springcloud.labx19.userapplication;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-19/labx-19-sc-config-user-application-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx19/userapplication/config/OrderProperties.java",
    "content": "package cn.iocoder.springcloud.labx19.userapplication.config;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-19/labx-19-sc-config-user-application-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx19/userapplication/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx19.userapplication.controller;\n\nimport cn.iocoder.springcloud.labx19.userapplication.config.OrderProperties;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.cloud.context.config.annotation.RefreshScope;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/demo\")\n@RefreshScope\npublic class DemoController {\n\n    @Autowired\n    private OrderProperties orderProperties;\n\n    /**\n     * 测试 @ConfigurationProperties 注解的配置属性类\n     */\n    @GetMapping(\"/test01\")\n    public OrderProperties test01() {\n        return orderProperties;\n    }\n\n    @Value(value = \"${order.pay-timeout-seconds}\")\n    private Integer payTimeoutSeconds;\n    @Value(value = \"${order.create-frequency-seconds}\")\n    private Integer createFrequencySeconds;\n\n    /**\n     * 测试 @Value 注解的属性\n     */\n    @GetMapping(\"/test02\")\n    public Map<String, Object> test02() {\n        Map<String, Object> result = new HashMap<>();\n        result.put(\"payTimeoutSeconds\", payTimeoutSeconds);\n        result.put(\"createFrequencySeconds\", createFrequencySeconds);\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "labx-19/labx-19-sc-config-user-application-auto-refresh-by-bus/src/main/resources/application.yml",
    "content": "# Kafka 配置项，对应 KafkaProperties 配置类\nkafka:\n  bootstrap-servers: 127.0.0.1:9092 # 指定 Kafka Broker 地址，可以设置多个，以逗号分隔\n"
  },
  {
    "path": "labx-19/labx-19-sc-config-user-application-auto-refresh-by-bus/src/main/resources/bootstrap.yml",
    "content": "spring:\n  application:\n    name: user-application\n\n  cloud:\n    # Spring Cloud Config Client 配置项，对应 ConfigClientProperties 类\n    config:\n      uri: http://127.0.0.1:8888 # Spring Cloud Config Server 的地址\n      name: ${spring.application.name} # 读取的配置文件的名字，默认为 ${spring.application.name}\n"
  },
  {
    "path": "labx-19/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-19</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-19-sc-bus-kafka-demo-publisher</module>\n        <module>labx-19-sc-bus-kafka-demo-listener</module>\n\n        <module>labx-19-sc-bus-kafka-demo-listener-actuator</module>\n        <module>labx-19-sc-bus-kafka-demo-publisher-actuator</module>\n\n        <module>labx-19-sc-config-server-git-auto-refresh-by-bus</module>\n        <module>labx-19-sc-config-user-application-auto-refresh-by-bus</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-19/《芋道 Spring Cloud 事件总线 Kafka 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/Bus-Kafka/?github>\n"
  },
  {
    "path": "labx-20/labx-20-sc-config-server-git-auto-refresh-by-bus/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-12</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-20-sc-config-server-git-auto-refresh-by-bus</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-config-server</artifactId>\n        </dependency>\n\n        <!-- 引入基于 RocketMQ 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-rocketmq</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Config Monitor 依赖，提供 `/monitor` 接口，用于刷新配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-config-monitor</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-20/labx-20-sc-config-server-git-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx20/configserverdemo/ConfigServerApplication.java",
    "content": "package cn.iocoder.springcloud.labx20.configserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.config.server.EnableConfigServer;\n\n@SpringBootApplication\n@EnableConfigServer\npublic class ConfigServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConfigServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-20/labx-20-sc-config-server-git-auto-refresh-by-bus/src/main/resources/application.yml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: demo-config-server\n\n  profiles:\n    active: git # 使用的 Spring Cloud Config Server 的存储器方案\n  # Spring Cloud Config 相关配置项\n  cloud:\n    config:\n      server:\n        # Spring Cloud Config Server 的 Git 存储器的配置项，对应 MultipleJGitEnvironmentProperties 类\n        git:\n          uri: https://github.com/YunaiV/demo-config-server.git # Git 仓库地址\n          search-paths: / # 读取文件的根地址\n          default-label: master # 使用的默认分支，默认为 master\n  #          username: ${CODING_USERNAME} # 账号\n  #          password: ${CODING_PASSWORD} # 密码\n\n# rocketmq 配置项，对应 RocketMQProperties 配置类\nrocketmq:\n  name-server: 127.0.0.1:9876 # RocketMQ Namesrv\n"
  },
  {
    "path": "labx-20/labx-20-sc-config-user-application-auto-refresh-by-bus/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-12</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-20-sc-config-user-application-auto-refresh-by-bus</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Config Client 相关依赖，作为配置中心的客户端，并实现自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-config</artifactId>\n        </dependency>\n\n        <!-- 引入基于 RocketMQ 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-rocketmq</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-20/labx-20-sc-config-user-application-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx20/userapplication/UserApplication.java",
    "content": "package cn.iocoder.springcloud.labx20.userapplication;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-20/labx-20-sc-config-user-application-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx20/userapplication/config/OrderProperties.java",
    "content": "package cn.iocoder.springcloud.labx20.userapplication.config;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-20/labx-20-sc-config-user-application-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx20/userapplication/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx20.userapplication.controller;\n\nimport cn.iocoder.springcloud.labx20.userapplication.config.OrderProperties;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.cloud.context.config.annotation.RefreshScope;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/demo\")\n@RefreshScope\npublic class DemoController {\n\n    @Autowired\n    private OrderProperties orderProperties;\n\n    /**\n     * 测试 @ConfigurationProperties 注解的配置属性类\n     */\n    @GetMapping(\"/test01\")\n    public OrderProperties test01() {\n        return orderProperties;\n    }\n\n    @Value(value = \"${order.pay-timeout-seconds}\")\n    private Integer payTimeoutSeconds;\n    @Value(value = \"${order.create-frequency-seconds}\")\n    private Integer createFrequencySeconds;\n\n    /**\n     * 测试 @Value 注解的属性\n     */\n    @GetMapping(\"/test02\")\n    public Map<String, Object> test02() {\n        Map<String, Object> result = new HashMap<>();\n        result.put(\"payTimeoutSeconds\", payTimeoutSeconds);\n        result.put(\"createFrequencySeconds\", createFrequencySeconds);\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "labx-20/labx-20-sc-config-user-application-auto-refresh-by-bus/src/main/resources/application.yml",
    "content": "# rocketmq 配置项，对应 RocketMQProperties 配置类\nrocketmq:\n  name-server: 127.0.0.1:9876 # RocketMQ Namesrv\n\nserver:\n  port: 8081\n"
  },
  {
    "path": "labx-20/labx-20-sc-config-user-application-auto-refresh-by-bus/src/main/resources/bootstrap.yml",
    "content": "spring:\n  application:\n    name: user-application\n\n  cloud:\n    # Spring Cloud Config Client 配置项，对应 ConfigClientProperties 类\n    config:\n      uri: http://127.0.0.1:8888 # Spring Cloud Config Server 的地址\n      name: ${spring.application.name} # 读取的配置文件的名字，默认为 ${spring.application.name}\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-listener/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-20</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-20-sca-bus-rocketmq-demo-listener</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入基于 RocketMQ 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-rocketmq</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-listener/src/main/java/cn/iocoder/springcloudalibaba/labx20/listenerdemo/ListenerDemoApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx20.listenerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.bus.jackson.RemoteApplicationEventScan;\n\n@SpringBootApplication\n@RemoteApplicationEventScan\npublic class ListenerDemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ListenerDemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-listener/src/main/java/cn/iocoder/springcloudalibaba/labx20/listenerdemo/event/UserRegisterEvent.java",
    "content": "package cn.iocoder.springcloudalibaba.labx20.listenerdemo.event;\n\nimport org.springframework.cloud.bus.event.RemoteApplicationEvent;\n\n/**\n * 用户注册事件\n */\npublic class UserRegisterEvent extends RemoteApplicationEvent {\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    public UserRegisterEvent() { // 序列化\n    }\n\n    public UserRegisterEvent(Object source, String originService, String destinationService, String username) {\n        super(source, originService);\n        this.username = username;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n}\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-listener/src/main/java/cn/iocoder/springcloudalibaba/labx20/listenerdemo/listener/UserRegisterListener.java",
    "content": "package cn.iocoder.springcloudalibaba.labx20.listenerdemo.listener;\n\nimport cn.iocoder.springcloudalibaba.labx20.listenerdemo.event.UserRegisterEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.stereotype.Component;\n\n/**\n * 用户注册事件的监听器\n */\n@Component\npublic class UserRegisterListener implements ApplicationListener<UserRegisterEvent> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void onApplicationEvent(UserRegisterEvent event) {\n        logger.info(\"[onApplicationEvent][监听到用户({}) 注册]\", event.getUsername());\n    }\n\n}\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-listener/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: listener-demo\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n\n# rocketmq 配置项，对应 RocketMQProperties 配置类\nrocketmq:\n  name-server: 127.0.0.1:9876 # RocketMQ Namesrv\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-listener-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-20</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-20-sca-bus-rocketmq-demo-listener-actuator</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入基于 RocketMQ 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-rocketmq</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-listener-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx20/listenerdemo/ListenerDemoApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx20.listenerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.bus.jackson.RemoteApplicationEventScan;\n\n@SpringBootApplication\n@RemoteApplicationEventScan\npublic class ListenerDemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ListenerDemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-listener-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx20/listenerdemo/event/UserRegisterEvent.java",
    "content": "package cn.iocoder.springcloudalibaba.labx20.listenerdemo.event;\n\nimport org.springframework.cloud.bus.event.RemoteApplicationEvent;\n\n/**\n * 用户注册事件\n */\npublic class UserRegisterEvent extends RemoteApplicationEvent {\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    public UserRegisterEvent() { // 序列化\n    }\n\n    public UserRegisterEvent(Object source, String originService, String destinationService, String username) {\n        super(source, originService);\n        this.username = username;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n}\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-listener-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx20/listenerdemo/listener/UserRegisterListener.java",
    "content": "package cn.iocoder.springcloudalibaba.labx20.listenerdemo.listener;\n\nimport cn.iocoder.springcloudalibaba.labx20.listenerdemo.event.UserRegisterEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.stereotype.Component;\n\n/**\n * 用户注册事件的监听器\n */\n@Component\npublic class UserRegisterListener implements ApplicationListener<UserRegisterEvent> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void onApplicationEvent(UserRegisterEvent event) {\n        logger.info(\"[onApplicationEvent][监听到用户({}) 注册]\", event.getUsername());\n    }\n\n}\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-listener-actuator/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: listener-demo\n\nserver:\n  port: 18080 # 随机端口，方便启动多个消费者\n\n# rocketmq 配置项，对应 RocketMQProperties 配置类\nrocketmq:\n  name-server: 127.0.0.1:9876 # RocketMQ Namesrv\n\nmanagement:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-publisher/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-20</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-20-sca-bus-rocketmq-demo-publisher</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入基于 Kafka 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-rocketmq</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-publisher/src/main/java/cn/iocoder/springcloudalibaba/labx20/publisherdemo/PublisherDemoApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx20.publisherdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n//@RemoteApplicationEventScan\npublic class PublisherDemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(PublisherDemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-publisher/src/main/java/cn/iocoder/springcloudalibaba/labx20/publisherdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloudalibaba.labx20.publisherdemo.controller;\n\nimport cn.iocoder.springcloudalibaba.labx20.publisherdemo.event.UserRegisterEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.bus.ServiceMatcher;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private ApplicationEventPublisher applicationEventPublisher;\n\n    @Autowired\n    private ServiceMatcher busServiceMatcher;\n\n    @GetMapping(\"/register\")\n    public String register(String username) {\n        // ... 执行注册逻辑\n        logger.info(\"[register][执行用户({}) 的注册逻辑]\", username);\n\n        // ... 发布\n        applicationEventPublisher.publishEvent(new UserRegisterEvent(this, busServiceMatcher.getServiceId(),\n                null, username));\n        return \"success\";\n    }\n\n}\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-publisher/src/main/java/cn/iocoder/springcloudalibaba/labx20/publisherdemo/event/UserRegisterEvent.java",
    "content": "package cn.iocoder.springcloudalibaba.labx20.publisherdemo.event;\n\nimport org.springframework.cloud.bus.event.RemoteApplicationEvent;\n\n/**\n * 用户注册事件\n */\npublic class UserRegisterEvent extends RemoteApplicationEvent {\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    public UserRegisterEvent() { // 序列化\n    }\n\n    public UserRegisterEvent(Object source, String originService, String destinationService, String username) {\n        super(source, originService);\n        this.username = username;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n}\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-publisher/src/main/resources/application.yml",
    "content": "server:\n  port: 8081\n\nspring:\n  application:\n    name: publisher-demo\n\n  # Bus 相关配置项，对应 BusProperties\n  cloud:\n    bus:\n      enabled: true # 是否开启，默认为 true\n      destination: springCloudBus # 目标消息队列，默认为 springCloudBus\n\n# rocketmq 配置项，对应 RocketMQProperties 配置类\nrocketmq:\n  name-server: 127.0.0.1:9876 # RocketMQ Namesrv\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-publisher-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-20</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-20-sca-bus-rocketmq-demo-publisher-actuator</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入基于 RocketMQ 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-bus-rocketmq</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-publisher-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx20/publisherdemo/PublisherDemoApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx20.publisherdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n//@RemoteApplicationEventScan\npublic class PublisherDemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(PublisherDemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-publisher-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx20/publisherdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloudalibaba.labx20.publisherdemo.controller;\n\nimport cn.iocoder.springcloudalibaba.labx20.publisherdemo.event.UserRegisterEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.bus.ServiceMatcher;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private ApplicationEventPublisher applicationEventPublisher;\n\n    @Autowired\n    private ServiceMatcher busServiceMatcher;\n\n    @GetMapping(\"/register\")\n    public String register(String username) {\n        // ... 执行注册逻辑\n        logger.info(\"[register][执行用户({}) 的注册逻辑]\", username);\n\n        // ... 发布\n        applicationEventPublisher.publishEvent(new UserRegisterEvent(this, busServiceMatcher.getServiceId(),\n                null, username));\n        return \"success\";\n    }\n\n\n}\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-publisher-actuator/src/main/java/cn/iocoder/springcloudalibaba/labx20/publisherdemo/event/UserRegisterEvent.java",
    "content": "package cn.iocoder.springcloudalibaba.labx20.publisherdemo.event;\n\nimport org.springframework.cloud.bus.event.RemoteApplicationEvent;\n\n/**\n * 用户注册事件\n */\npublic class UserRegisterEvent extends RemoteApplicationEvent {\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    public UserRegisterEvent() { // 序列化\n    }\n\n    public UserRegisterEvent(Object source, String originService, String destinationService, String username) {\n        super(source, originService);\n        this.username = username;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n}\n"
  },
  {
    "path": "labx-20/labx-20-sca-bus-rocketmq-demo-publisher-actuator/src/main/resources/application.yml",
    "content": "server:\n  port: 8081\n\nspring:\n  application:\n    name: publisher-demo\n\n# rocketmq 配置项，对应 RocketMQProperties 配置类\nrocketmq:\n  name-server: 127.0.0.1:9876 # RocketMQ Namesrv\n\nmanagement:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n"
  },
  {
    "path": "labx-20/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-20</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-20-sca-bus-rocketmq-demo-publisher</module>\n        <module>labx-20-sca-bus-rocketmq-demo-listener</module>\n\n        <module>labx-20-sca-bus-rocketmq-demo-publisher-actuator</module>\n        <module>labx-20-sca-bus-rocketmq-demo-listener-actuator</module>\n\n        <module>labx-20-sc-config-server-git-auto-refresh-by-bus</module>\n        <module>labx-20-sc-config-user-application-auto-refresh-by-bus</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-20/《芋道 Spring Cloud Alibaba 事件总线 Bus RocketMQ 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud-Alibaba/Bus-RocketMQ/?github>\n"
  },
  {
    "path": "labx-21/labx-21-sc-user-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-08</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-21-sc-user-service</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-21/labx-21-sc-user-service/src/main/java/cn/iocoder/springcloud/labx21/userservice/UserServiceApplication.java",
    "content": "package cn.iocoder.springcloud.labx21.userservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-21/labx-21-sc-user-service/src/main/java/cn/iocoder/springcloud/labx21/userservice/controller/UserController.java",
    "content": "package cn.iocoder.springcloud.labx21.userservice.controller;\n\nimport cn.iocoder.springcloud.labx21.userservice.dto.UserAddDTO;\nimport cn.iocoder.springcloud.labx21.userservice.dto.UserDTO;\nimport org.springframework.web.bind.annotation.*;\n\n@RestController\n@RequestMapping(\"/user\")\npublic class UserController {\n\n    @GetMapping(\"/get\")\n    public UserDTO get(@RequestParam(\"id\") Integer id) {\n        return new UserDTO().setId(id)\n                .setName(\"没有昵称：\" + id)\n                .setGender(id % 2 + 1); // 1 - 男；2 - 女\n    }\n\n    @PostMapping(\"/add\")\n    public Integer add(UserAddDTO addDTO) {\n        return (int) (System.currentTimeMillis() / 1000); // 嘿嘿，随便返回一个 id\n    }\n\n    @GetMapping(\"/sleep\")\n    public void sleep() throws InterruptedException {\n        Thread.sleep(10 * 1000L);\n    }\n\n}\n"
  },
  {
    "path": "labx-21/labx-21-sc-user-service/src/main/java/cn/iocoder/springcloud/labx21/userservice/dto/UserAddDTO.java",
    "content": "package cn.iocoder.springcloud.labx21.userservice.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户添加 DTO\n */\npublic class UserAddDTO implements Serializable {\n\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public String getName() {\n        return name;\n    }\n\n    public UserAddDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserAddDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-21/labx-21-sc-user-service/src/main/java/cn/iocoder/springcloud/labx21/userservice/dto/UserDTO.java",
    "content": "package cn.iocoder.springcloud.labx21.userservice.dto;\n\nimport java.io.Serializable;\n\n/**\n * 用户信息 DTO\n */\npublic class UserDTO implements Serializable {\n\n    /**\n     * 用户编号\n     */\n    private Integer id;\n    /**\n     * 昵称\n     */\n    private String name;\n    /**\n     * 性别\n     */\n    private Integer gender;\n\n    public Integer getId() {\n        return id;\n    }\n\n    public UserDTO setId(Integer id) {\n        this.id = id;\n        return this;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public UserDTO setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    public Integer getGender() {\n        return gender;\n    }\n\n    public UserDTO setGender(Integer gender) {\n        this.gender = gender;\n        return this;\n    }\n}\n"
  },
  {
    "path": "labx-21/labx-21-sc-user-service/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: user-service # Spring 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\nserver:\n  port: ${random.int[10000,19999]}  # 服务器端口。默认为 8080\n#  port: 18080  # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-21</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-21-sc-zuul-demo01</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Zuul 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo01/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulApplication.java",
    "content": "package cn.iocoder.springcloud.labx21.zuuldemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.zuul.EnableZuulProxy;\n\n@SpringBootApplication\n@EnableZuulProxy // 开启 Zuul 网关\npublic class ZuulApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ZuulApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo01/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: zuul-application\n\n# Zuul 配置项，对应 ZuulProperties 配置类\nzuul:\n  servlet-path: / # ZuulServlet 匹配的路径，默认为 /zuul\n  # 路由配置项，对应 ZuulRoute Map\n  routes:\n    route_yudaoyuanma:\n      path: /blog/**\n      url: http://www.iocoder.cn\n    route_oschina:\n      path: /oschina/**\n      url: https://www.oschina.net\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo02-registry/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-21</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-21-sc-zuul-demo02-registry</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Zuul 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo02-registry/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulApplication.java",
    "content": "package cn.iocoder.springcloud.labx21.zuuldemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.zuul.EnableZuulProxy;\n\n@SpringBootApplication\n@EnableZuulProxy // 开启 Zuul 网关\npublic class ZuulApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ZuulApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo02-registry/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: zuul-application\n\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\n# Zuul 配置项，对应 ZuulProperties 配置类\nzuul:\n  servlet-path: / # ZuulServlet 匹配的路径，默认为 /zuul\n  # 路由配置项，对应 ZuulRoute Map\n  routes:\n    route_yudaoyuanma:\n      path: /blog/**\n      url: http://www.iocoder.cn\n    route_oschina:\n      path: /oschina/**\n      url: https://www.oschina.net\n    route_users:\n      path: /users/**\n      service-id: user-service\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo03-config-apollo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-21</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-21-sc-zuul-demo03-config-apollo</artifactId>\n\n    <properties>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Zuul 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!--  引入 Apollo 客户端，内置对 Apollo 的自动化配置 -->\n        <dependency>\n            <groupId>com.ctrip.framework.apollo</groupId>\n            <artifactId>apollo-client</artifactId>\n            <version>1.5.1</version>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo03-config-apollo/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulApplication.java",
    "content": "package cn.iocoder.springcloud.labx21.zuuldemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.zuul.EnableZuulProxy;\n\n@SpringBootApplication\n@EnableZuulProxy // 开启 Zuul 网关\npublic class ZuulApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ZuulApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo03-config-apollo/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulPropertiesRefresher.java",
    "content": "package cn.iocoder.springcloud.labx21.zuuldemo;\n\nimport com.ctrip.framework.apollo.model.ConfigChangeEvent;\nimport com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.context.environment.EnvironmentChangeEvent;\nimport org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent;\nimport org.springframework.cloud.netflix.zuul.filters.RouteLocator;\nimport org.springframework.context.ApplicationContext;\nimport org.springframework.stereotype.Component;\n\n/**\n * 由 https://github.com/ctripcorp/apollo-use-cases/blob/master/spring-cloud-zuul 提供代码，感谢~\n */\n@Component\npublic class ZuulPropertiesRefresher {\n\n    private static final Logger logger = LoggerFactory.getLogger(ZuulPropertiesRefresher.class);\n\n    @Autowired\n    private ApplicationContext applicationContext;\n\n    @Autowired\n    private RouteLocator routeLocator;\n\n    @ApolloConfigChangeListener(interestedKeyPrefixes = \"zuul.\")\n    public void onChange(ConfigChangeEvent changeEvent) {\n        refreshZuulProperties(changeEvent);\n    }\n\n    private void refreshZuulProperties(ConfigChangeEvent changeEvent) {\n        logger.info(\"Refreshing zuul properties!\");\n\n        /*\n         * rebind configuration beans, e.g. ZuulProperties\n         * @see org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder#onApplicationEvent\n         */\n        this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys()));\n\n        /*\n         * refresh routes\n         * @see org.springframework.cloud.netflix.zuul.ZuulServerAutoConfiguration.ZuulRefreshListener#onApplicationEvent\n         */\n        this.applicationContext.publishEvent(new RoutesRefreshedEvent(routeLocator));\n\n        logger.info(\"Zuul properties refreshed!\");\n    }\n\n}\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo03-config-apollo/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: zuul-application\n\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\n# Apollo 相关配置项\napp:\n  id: ${spring.application.name} # 使用的 Apollo 的项目（应用）编号\napollo:\n  meta: http://127.0.0.1:8080 # Apollo Meta Server 地址\n  bootstrap:\n    enabled: true # 是否开启 Apollo 配置预加载功能。默认为 false。\n    eagerLoad:\n      enable: true # 是否开启 Apollo 支持日志级别的加载时机。默认为 false。\n    namespaces: application # 使用的 Apollo 的命名空间，默认为 application。\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo03-config-nacos/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-21</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-21-sc-zuul-demo03-config-nacos</artifactId>\n\n    <properties>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Zuul 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Config 相关依赖，将 Nacos 作为配置中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo03-config-nacos/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulApplication.java",
    "content": "package cn.iocoder.springcloud.labx21.zuuldemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.zuul.EnableZuulProxy;\n\n@SpringBootApplication\n@EnableZuulProxy // 开启 Zuul 网关\npublic class ZuulApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ZuulApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo03-config-nacos/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulRouteRefreshListener.java",
    "content": "package cn.iocoder.springcloud.labx21.zuuldemo;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.context.environment.EnvironmentChangeEvent;\nimport org.springframework.cloud.netflix.zuul.RoutesRefreshedEvent;\nimport org.springframework.cloud.netflix.zuul.filters.RouteLocator;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class ZuulRouteRefreshListener implements ApplicationListener<EnvironmentChangeEvent> {\n\n    private static final Logger logger = LoggerFactory.getLogger(ZuulRouteRefreshListener.class);\n\n    @Autowired\n    private ApplicationEventPublisher publisher;\n\n    @Autowired\n    private RouteLocator routeLocator;\n\n    @Override\n    public void onApplicationEvent(EnvironmentChangeEvent event) {\n        // 判断是否有 `zuul.` 配置变化\n        boolean zuulConfigUpdated = false;\n        for (String key : event.getKeys()) {\n            if (key.startsWith(\"zuul.\")) {\n                zuulConfigUpdated = true;\n                break;\n            }\n        }\n        if (!zuulConfigUpdated) {\n            return;\n        }\n\n        // 发布 RoutesRefreshedEvent 事件\n        this.publisher.publishEvent(new RoutesRefreshedEvent(routeLocator));\n        logger.info(\"发布 RoutesRefreshedEvent 事件完成，刷新 Zuul 路由\");\n    }\n\n}\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo03-config-nacos/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo03-config-nacos/src/main/resources/bootstrap.yaml",
    "content": "spring:\n  application:\n    name: zuul-application\n\n  cloud:\n    nacos:\n      # Nacos Config 配置项，对应 NacosConfigProperties 配置属性类\n      config:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n        namespace: # 使用的 Nacos 的命名空间，默认为 null\n        group: DEFAULT_GROUP # 使用的 Nacos 配置分组，默认为 DEFAULT_GROUP\n        name: # 使用的 Nacos 配置集的 dataId，默认为 spring.application.name\n        file-extension: yaml # 使用的 Nacos 配置集的 dataId 的文件拓展名，同时也是 Nacos 配置集的配置格式，默认为 properties\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo05-custom-zuul-filter/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-21</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-21-sc-zuul-demo05-custom-zuul-filter</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Zuul 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo05-custom-zuul-filter/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulApplication.java",
    "content": "package cn.iocoder.springcloud.labx21.zuuldemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.zuul.EnableZuulProxy;\n\n@SpringBootApplication\n@EnableZuulProxy // 开启 Zuul 网关\npublic class ZuulApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ZuulApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo05-custom-zuul-filter/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/filter/AuthZuulFilter.java",
    "content": "package cn.iocoder.springcloud.labx21.zuuldemo.filter;\n\nimport com.netflix.zuul.ZuulFilter;\nimport com.netflix.zuul.context.RequestContext;\nimport com.netflix.zuul.exception.ZuulException;\nimport org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.stereotype.Component;\nimport org.springframework.util.StringUtils;\n\nimport javax.servlet.http.HttpServletRequest;\nimport java.util.HashMap;\nimport java.util.Map;\n\n@Component\npublic class AuthZuulFilter extends ZuulFilter {\n\n    /**\n     * 外部请求 Header - token 认证令牌\n     */\n    private static final String DEFAULT_TOKEN_HEADER_NAME = \"token\";\n    /**\n     * 转发请求 Header - userId 用户编号\n     */\n    private static final String DEFAULT_HEADER_NAME = \"user-id\";\n\n    /**\n     * token 和 userId 的映射\n     */\n    private static Map<String, Integer> TOKENS = new HashMap<String, Integer>();\n\n    static {\n        TOKENS.put(\"yunai\", 1);\n    }\n\n    public String filterType() {\n        return FilterConstants.PRE_TYPE; // 前置过滤器\n    }\n\n    public int filterOrder() {\n        return 0;\n    }\n\n    public boolean shouldFilter() {\n        return true; // 需要过滤\n    }\n\n    public Object run() throws ZuulException {\n        // 获取当前请求上下文\n        RequestContext ctx = RequestContext.getCurrentContext();\n        // 获得 token\n        HttpServletRequest request = ctx.getRequest();\n        String token = request.getHeader(DEFAULT_TOKEN_HEADER_NAME);\n\n        // 如果没有 token，则不进行认证。因为可能是无需认证的 API 接口\n        if (!StringUtils.hasText(token)) {\n            return null;\n        }\n\n        // 进行认证\n        Integer userId = TOKENS.get(token);\n\n        // 通过 token 获取不到 userId，说明认证不通过\n        if (userId == null) {\n            ctx.setSendZuulResponse(false);\n            ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); // 响应 401 状态码\n            return null;\n        }\n\n        // 认证通过，将 userId 添加到 Header 中\n        ctx.getZuulRequestHeaders().put(DEFAULT_HEADER_NAME, String.valueOf(userId));\n        return null;\n    }\n\n}\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo05-custom-zuul-filter/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: zuul-application\n\n# Zuul 配置项，对应 ZuulProperties 配置类\nzuul:\n  servlet-path: / # ZuulServlet 匹配的路径，默认为 /zuul\n  # 路由配置项，对应 ZuulRoute Map\n  routes:\n    route_yudaoyuanma:\n      path: /blog/**\n      url: http://www.iocoder.cn\n    route_oschina:\n      path: /oschina/**\n      url: https://www.oschina.net\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo07-hystrix/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-21</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-21-sc-zuul-demo07-hystrix</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Zuul 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo07-hystrix/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulApplication.java",
    "content": "package cn.iocoder.springcloud.labx21.zuuldemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.zuul.EnableZuulProxy;\n\n@SpringBootApplication\n@EnableZuulProxy // 开启 Zuul 网关\npublic class ZuulApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ZuulApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo07-hystrix/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/fallback/ApiFallbackProvider.java",
    "content": "package cn.iocoder.springcloud.labx21.zuuldemo.fallback;\n\nimport org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.client.ClientHttpResponse;\nimport org.springframework.stereotype.Component;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\n\n@Component\npublic class ApiFallbackProvider implements FallbackProvider {\n\n    public String getRoute() {\n        return \"*\";\n    }\n\n    public ClientHttpResponse fallbackResponse(String route, final Throwable cause) {\n        return new ClientHttpResponse() {\n\n            public HttpStatus getStatusCode() {\n                return HttpStatus.OK;\n            }\n\n            public int getRawStatusCode() {\n                return HttpStatus.OK.value();\n            }\n\n            public String getStatusText() {\n                return HttpStatus.OK.getReasonPhrase();\n            }\n\n            public void close() {}\n\n            public InputStream getBody() { // 响应内容\n                String bodyText = String.format(\"{\\\"code\\\": 500,\\\"message\\\": \\\"Service unavailable:%s\\\"}\", cause.getMessage());\n                return new ByteArrayInputStream(bodyText.getBytes());\n            }\n\n            public HttpHeaders getHeaders() { // 响应头\n                HttpHeaders headers = new HttpHeaders();\n                headers.setContentType(MediaType.APPLICATION_JSON); // json 返回\n                return headers;\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo07-hystrix/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: zuul-application\n\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\n# Zuul 配置项，对应 ZuulProperties 配置类\nzuul:\n  servlet-path: / # ZuulServlet 匹配的路径，默认为 /zuul\n  # 路由配置项，对应 ZuulRoute Map\n  routes:\n    route_yudaoyuanma:\n      path: /blog/**\n      url: http://www.iocoder.cn\n    route_oschina:\n      path: /oschina/**\n      url: https://www.oschina.net\n    route_users:\n      path: /users/**\n      service-id: user-service\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo07-sentinel/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-21</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-21-sc-zuul-demo07-sentinel</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Zuul 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Sentinel 相关依赖，使用 Sentinel 提供服务保障，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo07-sentinel/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/CustomBlockFallbackProvider.java",
    "content": "package cn.iocoder.springcloud.labx21.zuuldemo;\n\nimport com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.BlockResponse;\nimport com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.ZuulBlockFallbackManager;\nimport com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.ZuulBlockFallbackProvider;\nimport com.alibaba.csp.sentinel.slots.block.BlockException;\nimport org.springframework.stereotype.Component;\n\nimport javax.annotation.PostConstruct;\n\n@Component\npublic class CustomBlockFallbackProvider implements ZuulBlockFallbackProvider {\n\n    @PostConstruct\n    public void init() {\n        ZuulBlockFallbackManager.registerProvider(this); // 注册到 ZuulBlockFallbackManager\n    }\n\n    public String getRoute() {\n        return \"*\";\n    }\n\n    public BlockResponse fallbackResponse(String route, Throwable cause) {\n        if (cause instanceof BlockException) {\n            return new BlockResponse(429, \"你被 Block 啦！\", route);\n        } else {\n            return new BlockResponse(500, \"系统异常\", route);\n        }\n    }\n\n}\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo07-sentinel/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulApplication.java",
    "content": "package cn.iocoder.springcloud.labx21.zuuldemo;\n\nimport com.alibaba.cloud.sentinel.gateway.ConfigConstants;\nimport com.alibaba.csp.sentinel.config.SentinelConfig;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.zuul.EnableZuulProxy;\n\n@SpringBootApplication\n@EnableZuulProxy // 开启 Zuul 网关\npublic class ZuulApplication {\n\n    public static void main(String[] args) {\n        System.setProperty(SentinelConfig.APP_TYPE, ConfigConstants.APP_TYPE_ZUUL_GATEWAY); // 【重点】设置应用类型为 Zuul\n        SpringApplication.run(ZuulApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo07-sentinel/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: zuul-application\n\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\n    sentinel:\n      eager: true # 是否饥饿加载。默认为 false 关闭\n      transport:\n        dashboard: localhost:7070 # 是否饥饿加载。\n      #      # 数据源的配置项\n      #      datasource:\n      #        ds1.file:\n      #          file: \"classpath: sentinel-gw-flow.json\"\n      #          ruleType: gw-flow\n      #        ds2.file:\n      #          file: \"classpath: sentinel-gw-api-group.json\"\n      #          ruleType: gw-api-group\n      # Sentinel 对 Zuul 的专属配置项，对应 SentinelZuulProperties 类\n      zuul:\n        order:\n          pre: 10000 # 前置过滤器 SentinelZuulPreFilter 的顺序\n          post: 1000 # 后置过滤器 SentinelZuulPostFilter 的顺序\n          error: -1 # 错误过滤器 SentinelZuulErrorFilter 的顺序\n\n# Zuul 配置项，对应 ZuulProperties 配置类\nzuul:\n  servlet-path: / # ZuulServlet 匹配的路径，默认为 /zuul\n  # 路由配置项，对应 ZuulRoute Map\n  routes:\n    yudaoyuanma: # 这是一个 Route 编号\n      path: /**\n      url: http://www.iocoder.cn\n\n\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo07-sentinel/src/main/resources/sentinel-gw-api-group.json",
    "content": "[\n  {\n    \"apiName\": \"yudaoyuanma_customized_api\",\n    \"predicateItems\": [\n      {\n        \"pattern\": \"/categories/**\",\n        \"matchStrategy\": 1\n      },\n      {\n        \"items\": [\n          {\n            \"pattern\": \"/Dubbo/good-collection/\",\n            \"matchStrategy\": 0\n          },\n          {\n            \"pattern\": \"/SkyWalking/**\",\n            \"matchStrategy\": 1\n          }\n        ]\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo07-sentinel/src/main/resources/sentinel-gw-flow.json",
    "content": "[\n  {\n    \"resource\": \"yudaoyuanma\",\n    \"count\": 3\n  },\n  {\n    \"resource\": \"yudaoyuanma_customized_api\",\n    \"count\": 1\n  }\n]\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo09-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-21</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-21-sc-zuul-demo09-actuator</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Zuul 相关依赖，使用它作为网关，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo09-actuator/src/main/java/cn/iocoder/springcloud/labx21/zuuldemo/ZuulApplication.java",
    "content": "package cn.iocoder.springcloud.labx21.zuuldemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.zuul.EnableZuulProxy;\n\n@SpringBootApplication\n@EnableZuulProxy // 开启 Zuul 网关\npublic class ZuulApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ZuulApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-21/labx-21-sc-zuul-demo09-actuator/src/main/resources/application.yaml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: zuul-application\n\n# Zuul 配置项，对应 ZuulProperties 配置类\nzuul:\n  servlet-path: / # ZuulServlet 匹配的路径，默认为 /zuul\n  # 路由配置项，对应 ZuulRoute Map\n  routes:\n    route_yudaoyuanma:\n      path: /blog/**\n      url: http://www.iocoder.cn\n    route_oschina:\n      path: /oschina/**\n      url: https://www.oschina.net\n\nmanagement:\n  endpoints:\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n  endpoint:\n    # Health 端点配置项，对应 HealthProperties 配置类\n    health:\n      enabled: true # 是否开启。默认为 true 开启。\n      show-details: ALWAYS # 何时显示完整的健康信息。默认为 NEVER 都不展示。可选 WHEN_AUTHORIZED 当经过授权的用户；可选 ALWAYS 总是展示。\n  server:\n    port: 18888 # 单独设置端口，因为 8888 端口全部给 Zuul 了\n"
  },
  {
    "path": "labx-21/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-21</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-21-sc-zuul-demo01</module>\n        <module>labx-21-sc-zuul-demo02-registry</module>\n        <module>labx-21-sc-zuul-demo03-config-apollo</module>\n        <module>labx-21-sc-zuul-demo03-config-nacos</module>\n        <module>labx-21-sc-zuul-demo05-custom-zuul-filter</module>\n        <module>labx-21-sc-zuul-demo07-hystrix</module>\n        <module>labx-21-sc-zuul-demo07-sentinel</module>\n        <module>labx-21-sc-zuul-demo09-actuator</module>\n\n        <module>labx-21-sc-user-service</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-21/《芋道 Spring Cloud 网关 Zuul 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/Netflix-Zuul/?github>\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-demo01-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-22</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-22-scn-eureka-demo01-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Netflix Eureka Client 相关依赖，将 Eureka 作为注册中心的客户端，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-demo01-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx22/consumerdemo/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx22.consumerdemo;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.List;\n\n@SpringBootApplication\n// @EnableDiscoveryClient\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n    @Configuration\n    public class RestTemplateConfiguration {\n\n        @Bean\n        public RestTemplate restTemplate() {\n            return new RestTemplate();\n        }\n\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private DiscoveryClient discoveryClient;\n        @Autowired\n        private RestTemplate restTemplate;\n        @Autowired\n        private LoadBalancerClient loadBalancerClient;\n\n        @GetMapping(\"/hello\")\n        public String hello(String name) {\n            // 获得服务 `demo-provider` 的一个实例\n            ServiceInstance instance;\n            if (true) {\n                // 获取服务 `demo-provider` 对应的实例列表\n                List<ServiceInstance> instances = discoveryClient.getInstances(\"demo-provider\");\n                // 选择第一个\n                instance = instances.size() > 0 ? instances.get(0) : null;\n            } else {\n                instance = loadBalancerClient.choose(\"demo-provider\");\n            }\n            // 发起调用\n            if (instance == null) {\n                throw new IllegalStateException(\"获取不到实例\");\n            }\n            String targetUrl = instance.getUri() + \"/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-demo01-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n\neureka:\n  client:\n    register-with-eureka: true\n    fetch-registry: true\n    service-url:\n      defaultZone: http://127.0.0.1:8761/eureka/\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-demo01-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-22</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-22-scn-eureka-demo01-provider</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Netflix Eureka Client 相关依赖，将 Eureka 作为注册中心的客户端，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-demo01-provider/src/main/java/cn/iocoder/springcloudalibaba/labx22/providerdemo/DemoProviderApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx22.providerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class DemoProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoProviderApplication.class, args);\n    }\n\n    @RestController\n    static class TestController {\n\n        @GetMapping(\"/echo\")\n        public String echo(String name) {\n            return \"provider:\" + name;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-demo01-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider # Spring 应用名\n\nserver:\n  port: 18080 # 服务器端口。默认为 8080\n\neureka:\n  client:\n    register-with-eureka: true # 注册到 Eureka-Server，默认为 true\n    fetch-registry: true # 从 Eureka-Server 获取注册表，默认为 true\n    service-url:\n      defaultZone: http://127.0.0.1:8761/eureka/ # Eureka-Server 地址\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-demo02-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-22</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-22-scn-eureka-demo02-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Netflix Eureka Client 相关依赖，将 Eureka 作为注册中心的客户端，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-demo02-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx22/consumerdemo/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx22.consumerdemo;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.List;\n\n@SpringBootApplication\n// @EnableDiscoveryClient\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n    @Configuration\n    public class RestTemplateConfiguration {\n\n        @Bean\n        public RestTemplate restTemplate() {\n            return new RestTemplate();\n        }\n\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private DiscoveryClient discoveryClient;\n        @Autowired\n        private RestTemplate restTemplate;\n        @Autowired\n        private LoadBalancerClient loadBalancerClient;\n\n        @GetMapping(\"/hello\")\n        public String hello(String name) {\n            // 获得服务 `demo-provider` 的一个实例\n            ServiceInstance instance;\n            if (true) {\n                // 获取服务 `demo-provider` 对应的实例列表\n                List<ServiceInstance> instances = discoveryClient.getInstances(\"demo-provider\");\n                // 选择第一个\n                instance = instances.size() > 0 ? instances.get(0) : null;\n            } else {\n                instance = loadBalancerClient.choose(\"demo-provider\");\n            }\n            // 发起调用\n            if (instance == null) {\n                throw new IllegalStateException(\"获取不到实例\");\n            }\n            String targetUrl = instance.getUri() + \"/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-demo02-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n\neureka:\n  client:\n    register-with-eureka: true # 注册到 Eureka-Server，默认为 true\n    fetch-registry: true # 从 Eureka-Server 获取注册表，默认为 true\n    service-url:\n      defaultZone: http://eureka-node01:18761/eureka/, http://eureka-node02:28761/eureka/\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-demo02-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-22</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-22-scn-eureka-demo02-provider</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Netflix Eureka Client 相关依赖，将 Eureka 作为注册中心的客户端，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-demo02-provider/src/main/java/cn/iocoder/springcloudalibaba/labx22/providerdemo/DemoProviderApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx22.providerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class DemoProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoProviderApplication.class, args);\n    }\n\n    @RestController\n    static class TestController {\n\n        @GetMapping(\"/echo\")\n        public String echo(String name) {\n            return \"provider:\" + name;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-demo02-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider # Spring 应用名\n\nserver:\n  port: 18080 # 服务器端口。默认为 8080\n\neureka:\n  client:\n    register-with-eureka: true # 注册到 Eureka-Server，默认为 true\n    fetch-registry: true # 从 Eureka-Server 获取注册表，默认为 true\n    service-url:\n      defaultZone: http://eureka-node01:18761/eureka/, http://eureka-node02:28761/eureka/\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-demo03-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-22</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-22-scn-eureka-demo03-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Netflix Eureka Client 相关依赖，将 Eureka 作为注册中心的客户端，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-demo03-consumer/src/main/java/cn/iocoder/springcloudalibaba/labx22/consumerdemo/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx22.consumerdemo;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.List;\n\n@SpringBootApplication\n// @EnableDiscoveryClient\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n    @Configuration\n    public class RestTemplateConfiguration {\n\n        @Bean\n        public RestTemplate restTemplate() {\n            return new RestTemplate();\n        }\n\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private DiscoveryClient discoveryClient;\n        @Autowired\n        private RestTemplate restTemplate;\n        @Autowired\n        private LoadBalancerClient loadBalancerClient;\n\n        @GetMapping(\"/hello\")\n        public String hello(String name) {\n            // 获得服务 `demo-provider` 的一个实例\n            ServiceInstance instance;\n            if (true) {\n                // 获取服务 `demo-provider` 对应的实例列表\n                List<ServiceInstance> instances = discoveryClient.getInstances(\"demo-provider\");\n                // 选择第一个\n                instance = instances.size() > 0 ? instances.get(0) : null;\n            } else {\n                instance = loadBalancerClient.choose(\"demo-provider\");\n            }\n            // 发起调用\n            if (instance == null) {\n                throw new IllegalStateException(\"获取不到实例\");\n            }\n            String targetUrl = instance.getUri() + \"/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-demo03-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n\neureka:\n  client:\n    register-with-eureka: true\n    fetch-registry: true\n    service-url:\n      defaultZone: http://eureka:woshimima@127.0.0.1:8761/eureka/ # Eureka-Server 地址\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-demo03-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-22</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-22-scn-eureka-demo03-provider</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Netflix Eureka Client 相关依赖，将 Eureka 作为注册中心的客户端，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-demo03-provider/src/main/java/cn/iocoder/springcloudalibaba/labx22/providerdemo/DemoProviderApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx22.providerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class DemoProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoProviderApplication.class, args);\n    }\n\n    @RestController\n    static class TestController {\n\n        @GetMapping(\"/echo\")\n        public String echo(String name) {\n            return \"provider:\" + name;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-demo03-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider # Spring 应用名\n\nserver:\n  port: 18080 # 服务器端口。默认为 8080\n\neureka:\n  client:\n    register-with-eureka: true # 注册到 Eureka-Server，默认为 true\n    fetch-registry: true # 从 Eureka-Server 获取注册表，默认为 true\n    service-url:\n      defaultZone: http://eureka:woshimima@127.0.0.1:8761/eureka/ # Eureka-Server 地址\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-server-cluster/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-22</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-22-scn-eureka-server-cluster</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Netflix Eureka Server 相关依赖，将 Eureka 作为注册中心的服务器，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-server-cluster/src/main/java/cn/iocoder/springcloudalibaba/labx22/eurekaserverdemo/EurekaServerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx22.eurekaserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n@SpringBootApplication\n@EnableEurekaServer\npublic class EurekaServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(EurekaServerApplication.class,args);\n    }\n\n}\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-server-cluster/src/main/resources/application-node01.yaml",
    "content": "server:\n  port: 18761\n\nspring:\n  application:\n    name: eureka-server\n\neureka:\n  instance:\n    hostname: eureka-node01\n  client:\n    register-with-eureka: true\n    fetch-registry: true\n    service-url:\n      defaultZone: http://eureka-node02:28761/eureka/\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-server-cluster/src/main/resources/application-node02.yaml",
    "content": "server:\n  port: 28761\n\nspring:\n  application:\n    name: eureka-server\n\neureka:\n  instance:\n    hostname: eureka-node02\n  client:\n    register-with-eureka: true\n    fetch-registry: true\n    service-url:\n      defaultZone: http://eureka-node01:18761/eureka/\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-server-security/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-22</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-22-scn-eureka-server-security</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Netflix Eureka Server 相关依赖，将 Eureka 作为注册中心的服务器，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>\n        </dependency>\n\n        <!-- 实现对 Spring Security 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-security</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-server-security/src/main/java/cn/iocoder/springcloudalibaba/labx22/eurekaserverdemo/EurekaServerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx22.eurekaserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n@SpringBootApplication\n@EnableEurekaServer\npublic class EurekaServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(EurekaServerApplication.class,args);\n    }\n\n}\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-server-security/src/main/java/cn/iocoder/springcloudalibaba/labx22/eurekaserverdemo/config/WebSecurityConfig.java",
    "content": "package cn.iocoder.springcloudalibaba.labx22.eurekaserverdemo.config;\n\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.security.config.annotation.web.builders.HttpSecurity;\nimport org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;\n\n@Configuration\npublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {\n\n    @Override\n    protected void configure(HttpSecurity http) throws Exception {\n        http.csrf().ignoringAntMatchers(\"/eureka/**\");\n        super.configure(http);\n    }\n\n}\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-server-security/src/main/resources/application.yaml",
    "content": "server:\n  port: 8761 # 设置 Eureka-Server 的端口\n\nspring:\n  application:\n    name: eureka-server\n\n  # 使用 Spring Security 创建默认认证账号\n  security:\n    user:\n      name: eureka\n      password: woshimima\n\neureka:\n  client:\n    register-with-eureka: false # 不注册到 Eureka-Server，默认为 true\n    fetch-registry: false # 不从 Eureka-Server 获取注册表，默认为 true\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-server-standalone/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-22</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-22-scn-eureka-server-standalone</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 Spring Cloud Netflix Eureka Server 相关依赖，将 Eureka 作为注册中心的服务器，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-server-standalone/src/main/java/cn/iocoder/springcloudalibaba/labx22/eurekaserverdemo/EurekaServerApplication.java",
    "content": "package cn.iocoder.springcloudalibaba.labx22.eurekaserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;\n\n@SpringBootApplication\n@EnableEurekaServer\npublic class EurekaServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(EurekaServerApplication.class,args);\n    }\n\n}\n"
  },
  {
    "path": "labx-22/labx-22-scn-eureka-server-standalone/src/main/resources/application.yaml",
    "content": "server:\n  port: 8761 # 设置 Eureka-Server 的端口\n\nspring:\n  application:\n    name: eureka-server\n\neureka:\n  client:\n    register-with-eureka: false # 不注册到 Eureka-Server，默认为 true\n    fetch-registry: false # 不从 Eureka-Server 获取注册表，默认为 true\n"
  },
  {
    "path": "labx-22/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-22</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-22-scn-eureka-server-standalone</module>\n        <module>labx-22-scn-eureka-demo01-provider</module>\n        <module>labx-22-scn-eureka-demo01-consumer</module>\n\n        <module>labx-22-scn-eureka-server-cluster</module>\n        <module>labx-22-scn-eureka-demo02-provider</module>\n        <module>labx-22-scn-eureka-demo02-consumer</module>\n\n        <module>labx-22-scn-eureka-server-security</module>\n        <module>labx-22-scn-eureka-demo03-provider</module>\n        <module>labx-22-scn-eureka-demo03-consumer</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-22/《芋道 Spring Cloud Netflix 注册中心 Eureka 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/Netflix-Eureka/?github>\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-23</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-23-scn-hystrix-actuator</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Netflix Hystrix 相关依赖，将 Hystrix 作为服务保障组件，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-actuator/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\n@EnableCircuitBreaker // 声明开启断路器\npublic class DemoApplication {\n\n    @Bean\n    public RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-actuator/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/CacheDemoController.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.controller;\n\nimport cn.iocoder.springcloud.labx23.hystrixdemo.service.CacheDemoService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/cache-demo\")\npublic class CacheDemoController {\n\n    @Autowired\n    private CacheDemoService cacheDemoService;\n\n    @GetMapping(\"/get_user\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        String userA = cacheDemoService.getUser(id);\n        String userB = cacheDemoService.getUser(id);\n        String userC = cacheDemoService.getUser(id);\n        return userC;\n    }\n\n    @GetMapping(\"/update_user\")\n    public String updateUser(@RequestParam(\"id\") Integer id) {\n        String userA = cacheDemoService.getUser(id);\n        cacheDemoService.updateUser(id);\n        String userC = cacheDemoService.getUser(id);\n        return userC;\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-actuator/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/CollapserDemoController.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.controller;\n\nimport cn.iocoder.springcloud.labx23.hystrixdemo.service.CollapserDemoService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\n\n@RestController\n@RequestMapping(\"/collapser-demo\")\npublic class CollapserDemoController {\n\n    private Logger logger = LoggerFactory.getLogger(CollapserDemoController.class);\n\n    @Autowired\n    private CollapserDemoService collapserDemoService;\n\n    @GetMapping(\"/test\")\n    public void test() throws ExecutionException, InterruptedException {\n        logger.info(\"[test][准备获取用户信息]\");\n        Future<String> user01 = collapserDemoService.getUserFuture(1);\n        Future<String> user02 = collapserDemoService.getUserFuture(2);\n        logger.info(\"[test][提交获取用户信息]\");\n\n        logger.info(\"[test][user({}) 的结果为({})]\", 1, user01.get());\n        logger.info(\"[test][user({}) 的结果为({})]\", 2, user02.get());\n    }\n\n\n    @GetMapping(\"/test_02\")\n    public void test02() throws ExecutionException, InterruptedException {\n        logger.info(\"[test][准备获取用户信息]\");\n        Future<String> user01 = collapserDemoService.getUserFuture(2);\n        Future<String> user02 = collapserDemoService.getUserFuture(1);\n        logger.info(\"[test][提交获取用户信息]\");\n\n        logger.info(\"[test][user({}) 的结果为({})]\", 1, user01.get());\n        logger.info(\"[test][user({}) 的结果为({})]\", 2, user02.get());\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-actuator/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.controller;\n\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @GetMapping(\"/get_user\")\n    @HystrixCommand(fallbackMethod = \"getUserFallback\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        logger.info(\"[getUser][准备调用 user-service 获取用户({})详情]\", id);\n        return restTemplate.getForEntity(\"http://127.0.0.1:18080/user/get?id=\" + id, String.class).getBody();\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, ExceptionUtils.getRootCauseMessage(throwable));\n        return \"mock:User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-actuator/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/filter/HystrixRequestContextFilter.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.filter;\n\nimport com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.*;\nimport javax.servlet.annotation.WebFilter;\nimport java.io.IOException;\n\n@Component\n@WebFilter(urlPatterns = \"/\")\npublic class HystrixRequestContextFilter implements Filter {\n\n    @Override\n    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n            throws IOException, ServletException {\n        // 初始化 HystrixRequestContext\n        HystrixRequestContext context = HystrixRequestContext.initializeContext();\n        // 继续过滤器\n        try {\n            chain.doFilter(request, response);\n        } finally {\n            // 销毁 HystrixRequestContext\n            context.close();\n        }\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-actuator/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/service/CacheDemoService.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.service;\n\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;\nimport com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.client.RestTemplate;\n\n@Service\npublic class CacheDemoService {\n\n    private Logger logger = LoggerFactory.getLogger(CacheDemoService.class);\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @HystrixCommand(fallbackMethod = \"getUserFallback\")\n    @CacheResult(cacheKeyMethod = \"genGetUserCacheKey\")\n    public String getUser(Integer id) {\n        logger.info(\"[getUser][准备调用 user-service 获取用户({})详情]\", id);\n        return restTemplate.getForEntity(\"http://127.0.0.1:18080/user/get?id=\" + id, String.class).getBody();\n    }\n\n    @HystrixCommand\n    @CacheRemove(commandKey = \"getUser\", cacheKeyMethod = \"genGetUserCacheKey\")\n    public void updateUser(Integer id) {\n        logger.info(\"[updateUser][更新用户({})详情]\", id);\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, ExceptionUtils.getRootCauseMessage(throwable));\n        return \"mock:User:\" + id;\n    }\n\n    public String genGetUserCacheKey(Integer id) {\n        return \"USER_\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-actuator/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/service/CollapserDemoService.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.service;\n\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.Future;\n\n@Service\npublic class CollapserDemoService {\n\n    private Logger logger = LoggerFactory.getLogger(CollapserDemoService.class);\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @HystrixCollapser(\n            batchMethod = \"getUsers\",\n            collapserProperties = {\n                    @HystrixProperty(name = \"timerDelayInMilliseconds\", value = \"10000\") // 演示，所以设置的时间较长\n            }\n    )\n    public Future<String> getUserFuture(Integer id) {\n        throw new RuntimeException(\"This method body should not be executed\");\n    }\n\n    @HystrixCommand\n    public List<String> getUsers(List<Integer> ids) {\n        logger.info(\"[getUsers][准备调用 user-service 获取多个用户({})详情]\", ids);\n        String[] users = restTemplate.getForEntity(\"http://127.0.0.1:18080/user/batch_get?ids=\" + StringUtils.join(ids, ',')\n                , String[].class).getBody();\n        return users == null || users.length == 0 ? Collections.emptyList() : Arrays.asList(users);\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-actuator/src/main/resources/application.yml",
    "content": "management:\n  endpoints:\n    web:\n      exposure:\n        include: 'hystrix.stream' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-dashboard/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-23</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-23-scn-hystrix-dashboard</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Hystrix Dashboard 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-dashboard/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/HystrixDashboardApplication.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;\n\n@SpringBootApplication\n@EnableHystrixDashboard // 声明开启 Hystrix Dashboard 功能\npublic class HystrixDashboardApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(HystrixDashboardApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-dashboard/src/main/resources/application.yml",
    "content": "server:\n  port: 9090\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-dashboard-turbine/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-23</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-23-scn-hystrix-dashboard-turbine</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 实现对 Hystrix Dashboard 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>\n        </dependency>\n\n        <!-- 实现对 Turbine 的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-turbine</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Netflix Eureka Client 相关依赖，将 Eureka 作为注册中心的客户端，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-dashboard-turbine/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/HystrixDashboardApplication.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;\nimport org.springframework.cloud.netflix.turbine.EnableTurbine;\n\n@SpringBootApplication\n@EnableHystrixDashboard // 声明开启 Hystrix Dashboard 功能\n@EnableTurbine // 声明开启 Turbine 功能\npublic class HystrixDashboardApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(HystrixDashboardApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-dashboard-turbine/src/main/resources/application.yml",
    "content": "server:\n  port: 9090\n\nspring:\n  application:\n    name: hystrix-dashboard\n\neureka:\n  client:\n    service-url:\n      defaultZone: http://127.0.0.1:8761/eureka/ # Eureka-Server 地址\n\n# Turbine 配置项，对应 TurbineProperties 配置类\nturbine:\n  app-config: hystrix-demo # 配置需要 Turbine 聚合的服务名；如果有多个，使用逗号分隔。\n  combine-host-port: true # 服务是否以 host + port 进行区分，默认为 true。如果设置为 false，则只以 host 进行区分，这样会导致相同主机部署了相同服务的多个实例，会被认为是一个\n  cluster-name-expression:  new String('default') # 指定集群名，设置为 `default` 表示默认集群。\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-demo01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-23</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-23-scn-hystrix-demo01</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Netflix Hystrix 相关依赖，将 Hystrix 作为服务保障组件，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-demo01/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\n@EnableCircuitBreaker // 声明开启断路器\npublic class DemoApplication {\n\n    @Bean\n    public RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-demo01/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/CacheDemoController.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.controller;\n\nimport cn.iocoder.springcloud.labx23.hystrixdemo.service.CacheDemoService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/cache-demo\")\npublic class CacheDemoController {\n\n    @Autowired\n    private CacheDemoService cacheDemoService;\n\n    @GetMapping(\"/get_user\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        String userA = cacheDemoService.getUser(id);\n        String userB = cacheDemoService.getUser(id);\n        String userC = cacheDemoService.getUser(id);\n        return userC;\n    }\n\n    @GetMapping(\"/update_user\")\n    public String updateUser(@RequestParam(\"id\") Integer id) {\n        String userA = cacheDemoService.getUser(id);\n        cacheDemoService.updateUser(id);\n        String userC = cacheDemoService.getUser(id);\n        return userC;\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-demo01/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/CollapserDemoController.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.controller;\n\nimport cn.iocoder.springcloud.labx23.hystrixdemo.service.CollapserDemoService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\n\n@RestController\n@RequestMapping(\"/collapser-demo\")\npublic class CollapserDemoController {\n\n    private Logger logger = LoggerFactory.getLogger(CollapserDemoController.class);\n\n    @Autowired\n    private CollapserDemoService collapserDemoService;\n\n    @GetMapping(\"/test\")\n    public void test() throws ExecutionException, InterruptedException {\n        logger.info(\"[test][准备获取用户信息]\");\n        Future<String> user01 = collapserDemoService.getUserFuture(1);\n        Future<String> user02 = collapserDemoService.getUserFuture(2);\n        logger.info(\"[test][提交获取用户信息]\");\n\n        logger.info(\"[test][user({}) 的结果为({})]\", 1, user01.get());\n        logger.info(\"[test][user({}) 的结果为({})]\", 2, user02.get());\n    }\n\n\n    @GetMapping(\"/test_02\")\n    public void test02() throws ExecutionException, InterruptedException {\n        logger.info(\"[test][准备获取用户信息]\");\n        Future<String> user01 = collapserDemoService.getUserFuture(2);\n        Future<String> user02 = collapserDemoService.getUserFuture(1);\n        logger.info(\"[test][提交获取用户信息]\");\n\n        logger.info(\"[test][user({}) 的结果为({})]\", 1, user01.get());\n        logger.info(\"[test][user({}) 的结果为({})]\", 2, user02.get());\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-demo01/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.controller;\n\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @GetMapping(\"/get_user\")\n    @HystrixCommand(fallbackMethod = \"getUserFallback\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        logger.info(\"[getUser][准备调用 user-service 获取用户({})详情]\", id);\n        return restTemplate.getForEntity(\"http://127.0.0.1:18080/user/get?id=\" + id, String.class).getBody();\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, ExceptionUtils.getRootCauseMessage(throwable));\n        return \"mock:User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-demo01/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/filter/HystrixRequestContextFilter.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.filter;\n\nimport com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.*;\nimport javax.servlet.annotation.WebFilter;\nimport java.io.IOException;\n\n@Component\n@WebFilter(urlPatterns = \"/\")\npublic class HystrixRequestContextFilter implements Filter {\n\n    @Override\n    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n            throws IOException, ServletException {\n        // 初始化 HystrixRequestContext\n        HystrixRequestContext context = HystrixRequestContext.initializeContext();\n        // 继续过滤器\n        try {\n            chain.doFilter(request, response);\n        } finally {\n            // 销毁 HystrixRequestContext\n            context.close();\n        }\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-demo01/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/service/CacheDemoService.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.service;\n\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;\nimport com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.client.RestTemplate;\n\n@Service\npublic class CacheDemoService {\n\n    private Logger logger = LoggerFactory.getLogger(CacheDemoService.class);\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @HystrixCommand(fallbackMethod = \"getUserFallback\")\n    @CacheResult(cacheKeyMethod = \"genGetUserCacheKey\")\n    public String getUser(Integer id) {\n        logger.info(\"[getUser][准备调用 user-service 获取用户({})详情]\", id);\n        return restTemplate.getForEntity(\"http://127.0.0.1:18080/user/get?id=\" + id, String.class).getBody();\n    }\n\n    @HystrixCommand\n    @CacheRemove(commandKey = \"getUser\", cacheKeyMethod = \"genGetUserCacheKey\")\n    public void updateUser(Integer id) {\n        logger.info(\"[updateUser][更新用户({})详情]\", id);\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, ExceptionUtils.getRootCauseMessage(throwable));\n        return \"mock:User:\" + id;\n    }\n\n    public String genGetUserCacheKey(Integer id) {\n        return \"USER_\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-demo01/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/service/CollapserDemoService.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.service;\n\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.Future;\n\n@Service\npublic class CollapserDemoService {\n\n    private Logger logger = LoggerFactory.getLogger(CollapserDemoService.class);\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @HystrixCollapser(\n            batchMethod = \"getUsers\",\n            collapserProperties = {\n                    @HystrixProperty(name = \"timerDelayInMilliseconds\", value = \"10000\") // 演示，所以设置的时间较长\n            }\n    )\n    public Future<String> getUserFuture(Integer id) {\n        throw new RuntimeException(\"This method body should not be executed\");\n    }\n\n    @HystrixCommand\n    public List<String> getUsers(List<Integer> ids) {\n        logger.info(\"[getUsers][准备调用 user-service 获取多个用户({})详情]\", ids);\n        String[] users = restTemplate.getForEntity(\"http://127.0.0.1:18080/user/batch_get?ids=\" + StringUtils.join(ids, ',')\n                , String[].class).getBody();\n        return users == null || users.length == 0 ? Collections.emptyList() : Arrays.asList(users);\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-demo01-cluster/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-23</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-23-scn-hystrix-demo01-cluster</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Netflix Hystrix 相关依赖，将 Hystrix 作为服务保障组件，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Netflix Eureka Client 相关依赖，将 Eureka 作为注册中心的客户端，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-demo01-cluster/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\n@EnableCircuitBreaker // 声明开启断路器\npublic class DemoApplication {\n\n    @Bean\n    public RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-demo01-cluster/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/CacheDemoController.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.controller;\n\nimport cn.iocoder.springcloud.labx23.hystrixdemo.service.CacheDemoService;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/cache-demo\")\npublic class CacheDemoController {\n\n    @Autowired\n    private CacheDemoService cacheDemoService;\n\n    @GetMapping(\"/get_user\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        String userA = cacheDemoService.getUser(id);\n        String userB = cacheDemoService.getUser(id);\n        String userC = cacheDemoService.getUser(id);\n        return userC;\n    }\n\n    @GetMapping(\"/update_user\")\n    public String updateUser(@RequestParam(\"id\") Integer id) {\n        String userA = cacheDemoService.getUser(id);\n        cacheDemoService.updateUser(id);\n        String userC = cacheDemoService.getUser(id);\n        return userC;\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-demo01-cluster/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/CollapserDemoController.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.controller;\n\nimport cn.iocoder.springcloud.labx23.hystrixdemo.service.CollapserDemoService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.Future;\n\n@RestController\n@RequestMapping(\"/collapser-demo\")\npublic class CollapserDemoController {\n\n    private Logger logger = LoggerFactory.getLogger(CollapserDemoController.class);\n\n    @Autowired\n    private CollapserDemoService collapserDemoService;\n\n    @GetMapping(\"/test\")\n    public void test() throws ExecutionException, InterruptedException {\n        logger.info(\"[test][准备获取用户信息]\");\n        Future<String> user01 = collapserDemoService.getUserFuture(1);\n        Future<String> user02 = collapserDemoService.getUserFuture(2);\n        logger.info(\"[test][提交获取用户信息]\");\n\n        logger.info(\"[test][user({}) 的结果为({})]\", 1, user01.get());\n        logger.info(\"[test][user({}) 的结果为({})]\", 2, user02.get());\n    }\n\n\n    @GetMapping(\"/test_02\")\n    public void test02() throws ExecutionException, InterruptedException {\n        logger.info(\"[test][准备获取用户信息]\");\n        Future<String> user01 = collapserDemoService.getUserFuture(2);\n        Future<String> user02 = collapserDemoService.getUserFuture(1);\n        logger.info(\"[test][提交获取用户信息]\");\n\n        logger.info(\"[test][user({}) 的结果为({})]\", 1, user01.get());\n        logger.info(\"[test][user({}) 的结果为({})]\", 2, user02.get());\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-demo01-cluster/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.controller;\n\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @GetMapping(\"/get_user\")\n    @HystrixCommand(fallbackMethod = \"getUserFallback\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        logger.info(\"[getUser][准备调用 user-service 获取用户({})详情]\", id);\n        return restTemplate.getForEntity(\"http://127.0.0.1:18080/user/get?id=\" + id, String.class).getBody();\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, ExceptionUtils.getRootCauseMessage(throwable));\n        return \"mock:User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-demo01-cluster/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/filter/HystrixRequestContextFilter.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.filter;\n\nimport com.netflix.hystrix.strategy.concurrency.HystrixRequestContext;\nimport org.springframework.stereotype.Component;\n\nimport javax.servlet.*;\nimport javax.servlet.annotation.WebFilter;\nimport java.io.IOException;\n\n@Component\n@WebFilter(urlPatterns = \"/\")\npublic class HystrixRequestContextFilter implements Filter {\n\n    @Override\n    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)\n            throws IOException, ServletException {\n        // 初始化 HystrixRequestContext\n        HystrixRequestContext context = HystrixRequestContext.initializeContext();\n        // 继续过滤器\n        try {\n            chain.doFilter(request, response);\n        } finally {\n            // 销毁 HystrixRequestContext\n            context.close();\n        }\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-demo01-cluster/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/service/CacheDemoService.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.service;\n\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport com.netflix.hystrix.contrib.javanica.cache.annotation.CacheRemove;\nimport com.netflix.hystrix.contrib.javanica.cache.annotation.CacheResult;\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.client.RestTemplate;\n\n@Service\npublic class CacheDemoService {\n\n    private Logger logger = LoggerFactory.getLogger(CacheDemoService.class);\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @HystrixCommand(fallbackMethod = \"getUserFallback\")\n    @CacheResult(cacheKeyMethod = \"genGetUserCacheKey\")\n    public String getUser(Integer id) {\n        logger.info(\"[getUser][准备调用 user-service 获取用户({})详情]\", id);\n        return restTemplate.getForEntity(\"http://127.0.0.1:18080/user/get?id=\" + id, String.class).getBody();\n    }\n\n    @HystrixCommand\n    @CacheRemove(commandKey = \"getUser\", cacheKeyMethod = \"genGetUserCacheKey\")\n    public void updateUser(Integer id) {\n        logger.info(\"[updateUser][更新用户({})详情]\", id);\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, ExceptionUtils.getRootCauseMessage(throwable));\n        return \"mock:User:\" + id;\n    }\n\n    public String genGetUserCacheKey(Integer id) {\n        return \"USER_\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-demo01-cluster/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/service/CollapserDemoService.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.service;\n\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;\nimport org.apache.commons.lang.StringUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.concurrent.Future;\n\n@Service\npublic class CollapserDemoService {\n\n    private Logger logger = LoggerFactory.getLogger(CollapserDemoService.class);\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @HystrixCollapser(\n            batchMethod = \"getUsers\",\n            collapserProperties = {\n                    @HystrixProperty(name = \"timerDelayInMilliseconds\", value = \"10000\") // 演示，所以设置的时间较长\n            }\n    )\n    public Future<String> getUserFuture(Integer id) {\n        throw new RuntimeException(\"This method body should not be executed\");\n    }\n\n    @HystrixCommand\n    public List<String> getUsers(List<Integer> ids) {\n        logger.info(\"[getUsers][准备调用 user-service 获取多个用户({})详情]\", ids);\n        String[] users = restTemplate.getForEntity(\"http://127.0.0.1:18080/user/batch_get?ids=\" + StringUtils.join(ids, ',')\n                , String[].class).getBody();\n        return users == null || users.length == 0 ? Collections.emptyList() : Arrays.asList(users);\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-demo01-cluster/src/main/resources/application.yml",
    "content": "management:\n  endpoints:\n    web:\n      exposure:\n        include: 'hystrix.stream' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n\nspring:\n  application:\n    name: hystrix-demo # 应用名\n\neureka:\n  client:\n    service-url:\n      defaultZone: http://127.0.0.1:8761/eureka/ # Eureka-Server 地址\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-dubbo-demo/labx-23-scn-hystrix-dubbo-demo-application/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-23-scn-hystrix-dubbo-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-23-scn-hystrix-dubbo-demo-application</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入用户服务 API 包 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-23-scn-hystrix-dubbo-demo-user-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Netflix Hystrix 相关依赖，将 Hystrix 作为服务保障组件，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-dubbo-demo/labx-23-scn-hystrix-dubbo-demo-application/src/main/java/cn/iocoder/springcloud/labx23/demo/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx23.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;\n\n@SpringBootApplication\n@EnableCircuitBreaker // 声明开启断路器\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-dubbo-demo/labx-23-scn-hystrix-dubbo-demo-application/src/main/java/cn/iocoder/springcloud/labx23/demo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx23.demo.controller;\n\nimport cn.iocoder.springcloud.labx23.userservice.api.UserService;\nimport com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.apache.dubbo.config.annotation.Reference;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Reference(protocol = \"dubbo\", version = \"1.0.0\")\n    private UserService userService;\n\n    @GetMapping(\"/get_user\")\n    @HystrixCommand(fallbackMethod = \"getUserFallback\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        logger.info(\"[getUser][准备调用 user-service 获取用户({})详情]\", id);\n        return userService.getUser(id);\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, ExceptionUtils.getRootCauseMessage(throwable));\n        return \"mock:User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-dubbo-demo/labx-23-scn-hystrix-dubbo-demo-application/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848\n\n# Dubbo 配置项，对应 DubboConfigurationProperties 类\ndubbo:\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: user-service # 设置订阅的应用列表，默认为 * 订阅所有应用。\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-dubbo-demo/labx-23-scn-hystrix-dubbo-demo-user-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-23-scn-hystrix-dubbo-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-23-scn-hystrix-dubbo-demo-user-service</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入用户服务 API 包 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-23-scn-hystrix-dubbo-demo-user-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Dubbo 相关依赖，实现呢 Dubbo 进行远程调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-dubbo</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-dubbo-demo/labx-23-scn-hystrix-dubbo-demo-user-service/src/main/java/cn/iocoder/springcloud/labx23/userservice/UserServiceApplication.java",
    "content": "package cn.iocoder.springcloud.labx23.userservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserServiceApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-dubbo-demo/labx-23-scn-hystrix-dubbo-demo-user-service/src/main/java/cn/iocoder/springcloud/labx23/userservice/service/UserServiceImpl.java",
    "content": "package cn.iocoder.springcloud.labx23.userservice.service;\n\nimport cn.iocoder.springcloud.labx23.userservice.api.UserService;\n\n@org.apache.dubbo.config.annotation.Service(protocol = \"dubbo\", version = \"1.0.0\")\npublic class UserServiceImpl implements UserService {\n\n    @Override\n    public String getUser(Integer id) {\n        return \"User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-dubbo-demo/labx-23-scn-hystrix-dubbo-demo-user-service/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: user-service\n  cloud:\n    # Nacos 作为注册中心的配置项\n    nacos:\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\n# Dubbo 配置项，对应 DubboConfigurationProperties 类\ndubbo:\n  scan:\n    base-packages: cn.iocoder.springcloud.labx23.userservice.service # 指定 Dubbo 服务实现类的扫描基准包\n  # Dubbo 服务暴露的协议配置，对应 ProtocolConfig Map\n  protocols:\n    dubbo:\n      name: dubbo # 协议名称\n      port: -1 # 协议端口，-1 表示自增端口，从 20880 开始\n  # Dubbo 服务注册中心配置，对应 RegistryConfig 类\n  registry:\n    address: spring-cloud://127.0.0.1:8848 # 指定 Dubbo 服务注册中心的地址\n  # Spring Cloud Alibaba Dubbo 专属配置项，对应 DubboCloudProperties 类\n  cloud:\n    subscribed-services: '' # 设置订阅的应用列表，默认为 * 订阅所有应用。\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-dubbo-demo/labx-23-scn-hystrix-dubbo-demo-user-service-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-23-scn-hystrix-dubbo-demo</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-23-scn-hystrix-dubbo-demo-user-service-api</artifactId>\n\n</project>\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-dubbo-demo/labx-23-scn-hystrix-dubbo-demo-user-service-api/src/main/java/cn/iocoder/springcloud/labx23/userservice/api/UserService.java",
    "content": "package cn.iocoder.springcloud.labx23.userservice.api;\n\npublic interface UserService {\n\n    String getUser(Integer id);\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-dubbo-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-23</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-23-scn-hystrix-dubbo-demo</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-23-scn-hystrix-dubbo-demo-user-service</module>\n        <module>labx-23-scn-hystrix-dubbo-demo-user-service-api</module>\n        <module>labx-23-scn-hystrix-dubbo-demo-application</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-feign/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-23</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-23-scn-hystrix-feign</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Netflix Hystrix 相关依赖，将 Hystrix 作为服务保障组件，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud OpenFeign 相关依赖，使用 OpenFeign 提供声明式调用，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-openfeign</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-feign/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;\nimport org.springframework.cloud.openfeign.EnableFeignClients;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\n@EnableCircuitBreaker // 声明开启断路器\n@EnableFeignClients // 开启 Feign Client 功能\npublic class DemoApplication {\n\n    @Bean\n    public RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-feign/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/controller/FeignDemoController.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.controller;\n\nimport cn.iocoder.springcloud.labx23.hystrixdemo.feign.UserServiceFeignClient;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/feign-demo\")\npublic class FeignDemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private UserServiceFeignClient userServiceFeignClient;\n\n    @GetMapping(\"/get_user\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        logger.info(\"[getUser][准备调用 user-service 获取用户({})详情]\", id);\n        return userServiceFeignClient.getUser(id);\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-feign/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/fallback/UserServiceFeignClientFallbackFactory.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.fallback;\n\nimport cn.iocoder.springcloud.labx23.hystrixdemo.feign.UserServiceFeignClient;\nimport feign.hystrix.FallbackFactory;\nimport org.apache.commons.lang.exception.ExceptionUtils;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class UserServiceFeignClientFallbackFactory implements FallbackFactory<UserServiceFeignClient> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public UserServiceFeignClient create(Throwable cause) {\n        return new UserServiceFeignClient() {\n\n            @Override\n            public String getUser(Integer id) {\n                logger.info(\"[getUserFallback][id({}) exception({})]\", id, ExceptionUtils.getRootCauseMessage(cause));\n                return \"mock:User:\" + id;\n            }\n\n        };\n    }\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-feign/src/main/java/cn/iocoder/springcloud/labx23/hystrixdemo/feign/UserServiceFeignClient.java",
    "content": "package cn.iocoder.springcloud.labx23.hystrixdemo.feign;\n\nimport cn.iocoder.springcloud.labx23.hystrixdemo.fallback.UserServiceFeignClientFallbackFactory;\nimport org.springframework.cloud.openfeign.FeignClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\n\n@FeignClient(name = \"user-service\", url = \"http://127.0.0.1:18080\", fallbackFactory = UserServiceFeignClientFallbackFactory.class)\npublic interface UserServiceFeignClient {\n\n    @GetMapping(\"/user/get\")\n    String getUser(@RequestParam(\"id\") Integer id);\n\n}\n"
  },
  {
    "path": "labx-23/labx-23-scn-hystrix-feign/src/main/resources/application.yaml",
    "content": "feign:\n  hystrix:\n    enabled: true # 开启 Hystrix 对 Feign 的支持，默认为 false 关闭。\n"
  },
  {
    "path": "labx-23/labx-23-user-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-23</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-23-user-service</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-23/labx-23-user-service/src/main/java/cn/iocoder/springcloud/labx23/userservice/UserServiceApplication.java",
    "content": "package cn.iocoder.springcloud.labx23.userservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    @RestController\n    @RequestMapping(\"/user\")\n    public class UserController {\n\n        @GetMapping(\"/get\")\n        public String get(@RequestParam(\"id\") Integer id) {\n            return \"User:\" + id;\n        }\n\n        @GetMapping(\"/batch_get\")\n        public List<String> batchGet(@RequestParam(\"ids\") List<Integer> ids) {\n            return ids.stream().map(id -> \"User:\" + id).collect(Collectors.toList());\n        }\n\n    }\n\n    public static void main(String[] args) {\n        // 设置端口\n        System.setProperty(\"server.port\", \"18080\");\n\n        // 应用启动\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-23/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-23</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-23-user-service</module>\n\n        <module>labx-23-scn-hystrix-demo01</module>\n\n        <module>labx-23-scn-hystrix-actuator</module>\n        <module>labx-23-scn-hystrix-dashboard</module>\n\n        <module>labx-23-scn-hystrix-demo01-cluster</module>\n        <module>labx-23-scn-hystrix-dashboard-turbine</module>\n\n        <module>labx-23-scn-hystrix-feign</module>\n\n        <module>labx-23-scn-hystrix-dubbo-demo</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-23/《芋道 Spring Cloud Netflix 服务容错 Hystrix 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/Netflix-Hystrix/?github>\n"
  },
  {
    "path": "labx-24/labx-24-resilience4j-demo01/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-59</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-24-resilience4j-demo01</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Resilience4j Spring Cloud 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>io.github.resilience4j</groupId>\n            <artifactId>resilience4j-spring-cloud2</artifactId>\n            <version>1.4.0</version>\n        </dependency>\n\n        <!-- 引入 Aspectj 依赖，支持 AOP 相关注解、表达式等等 -->\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjrt</artifactId>\n            <version>1.9.5</version>\n        </dependency>\n        <dependency>\n            <groupId>org.aspectj</groupId>\n            <artifactId>aspectjweaver</artifactId>\n            <version>1.9.5</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-24/labx-24-resilience4j-demo01/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx24.resilience4jdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    @Bean\n    public RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-24/labx-24-resilience4j-demo01/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/controller/BulkheadDemoController.java",
    "content": "package cn.iocoder.springcloud.labx24.resilience4jdemo.controller;\n\nimport io.github.resilience4j.bulkhead.annotation.Bulkhead;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/bulkhead-demo\")\npublic class BulkheadDemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/get_user\")\n    @Bulkhead(name = \"backendC\", fallbackMethod = \"getUserFallback\", type = Bulkhead.Type.SEMAPHORE)\n    public String getUser(@RequestParam(\"id\") Integer id) throws InterruptedException {\n        logger.info(\"[getUser][id({})]\", id);\n        Thread.sleep(10 * 1000L); // sleep 10 秒\n        return \"User:\" + id;\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, throwable.getClass().getSimpleName());\n        return \"mock:User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-24/labx-24-resilience4j-demo01/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx24.resilience4jdemo.controller;\n\nimport io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @GetMapping(\"/get_user\")\n    @CircuitBreaker(name = \"backendA\", fallbackMethod = \"getUserFallback\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        logger.info(\"[getUser][准备调用 user-service 获取用户({})详情]\", id);\n        return restTemplate.getForEntity(\"http://127.0.0.1:18080/user/get?id=\" + id, String.class).getBody();\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, throwable.getClass().getSimpleName());\n        return \"mock:User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-24/labx-24-resilience4j-demo01/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/controller/RateLimiterDemoController.java",
    "content": "package cn.iocoder.springcloud.labx24.resilience4jdemo.controller;\n\nimport io.github.resilience4j.ratelimiter.annotation.RateLimiter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/rate-limiter-demo\")\npublic class RateLimiterDemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @GetMapping(\"/get_user\")\n    @RateLimiter(name = \"backendB\", fallbackMethod = \"getUserFallback\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        return \"User:\" + id;\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, throwable.getClass().getSimpleName());\n        return \"mock:User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-24/labx-24-resilience4j-demo01/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/controller/RetryDemoController.java",
    "content": "package cn.iocoder.springcloud.labx24.resilience4jdemo.controller;\n\nimport io.github.resilience4j.retry.annotation.Retry;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\n@RestController\n@RequestMapping(\"/retry-demo\")\npublic class RetryDemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @GetMapping(\"/get_user\")\n    @Retry(name = \"backendE\", fallbackMethod = \"getUserFallback\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        logger.info(\"[getUser][准备调用 user-service 获取用户({})详情]\", id);\n        return restTemplate.getForEntity(\"http://127.0.0.1:18080/user/get?id=\" + id, String.class).getBody();\n    }\n\n    public String getUserFallback(Integer id, Throwable throwable) {\n        logger.info(\"[getUserFallback][id({}) exception({})]\", id, throwable.getClass().getSimpleName());\n        return \"mock:User:\" + id;\n    }\n\n}\n"
  },
  {
    "path": "labx-24/labx-24-resilience4j-demo01/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/controller/ThreadPoolBulkheadDemoController.java",
    "content": "package cn.iocoder.springcloud.labx24.resilience4jdemo.controller;\n\nimport io.github.resilience4j.bulkhead.annotation.Bulkhead;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\n\n@RestController\n@RequestMapping(\"/thread-pool-bulkhead-demo\")\npublic class ThreadPoolBulkheadDemoController {\n\n    @Autowired\n    private ThreadPoolBulkheadService threadPoolBulkheadService;\n\n    @GetMapping(\"/get_user\")\n    public String getUser(@RequestParam(\"id\") Integer id) throws ExecutionException, InterruptedException {\n        threadPoolBulkheadService.getUser0(id);\n        return threadPoolBulkheadService.getUser0(id).get();\n    }\n\n    @Service\n    public static class ThreadPoolBulkheadService {\n\n        private Logger logger = LoggerFactory.getLogger(ThreadPoolBulkheadService.class);\n\n        @Bulkhead(name = \"backendD\", fallbackMethod = \"getUserFallback\", type = Bulkhead.Type.THREADPOOL)\n        public CompletableFuture<String> getUser0(Integer id) throws InterruptedException {\n            logger.info(\"[getUser][id({})]\", id);\n            Thread.sleep(10 * 1000L); // sleep 10 秒\n            return CompletableFuture.completedFuture(\"User:\" + id);\n        }\n\n        public CompletableFuture<String> getUserFallback(Integer id, Throwable throwable) {\n            logger.info(\"[getUserFallback][id({}) exception({})]\", id, throwable.getClass().getSimpleName());\n            return CompletableFuture.completedFuture(\"mock:User:\" + id);\n        }\n\n    }\n\n\n}\n"
  },
  {
    "path": "labx-24/labx-24-resilience4j-demo01/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/controller/TimeLimiterDemoController.java",
    "content": "package cn.iocoder.springcloud.labx24.resilience4jdemo.controller;\n\nimport io.github.resilience4j.bulkhead.annotation.Bulkhead;\nimport io.github.resilience4j.timelimiter.annotation.TimeLimiter;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Service;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\n\n@RestController\n@RequestMapping(\"/time-limiter-demo\")\npublic class TimeLimiterDemoController {\n\n    @Autowired\n    private TimeLimiterService timeLimiterService;\n\n    @GetMapping(\"/get_user\")\n    public String getUser(@RequestParam(\"id\") Integer id) throws ExecutionException, InterruptedException {\n        return timeLimiterService.getUser0(id).get();\n    }\n\n    @Service\n    public static class TimeLimiterService {\n\n        private Logger logger = LoggerFactory.getLogger(TimeLimiterService.class);\n\n        @Bulkhead(name = \"backendD\", type = Bulkhead.Type.THREADPOOL)\n        @TimeLimiter(name = \"backendF\", fallbackMethod = \"getUserFallback\")\n        public CompletableFuture<String> getUser0(Integer id) throws InterruptedException {\n            logger.info(\"[getUser][id({})]\", id);\n            Thread.sleep(10 * 1000L); // sleep 10 秒\n            return CompletableFuture.completedFuture(\"User:\" + id);\n        }\n\n        public CompletableFuture<String> getUserFallback(Integer id, Throwable throwable) {\n            logger.info(\"[getUserFallback][id({}) exception({})]\", id, throwable.getClass().getSimpleName());\n            return CompletableFuture.completedFuture(\"mock:User:\" + id);\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-24/labx-24-resilience4j-demo01/src/main/resources/application.yml",
    "content": "resilience4j:\n  # Resilience4j 的断路器配置项，对应 CircuitBreakerProperties 属性类\n  circuitbreaker:\n    instances:\n      backendA:\n        failure-rate-threshold: 50 # 熔断器关闭状态和半开状态使用的同一个失败率阈值，单位：百分比。默认为 50\n        ring-buffer-size-in-closed-state: 5 # 熔断器关闭状态的缓冲区大小，不会限制线程的并发量，在熔断器发生状态转换前所有请求都会调用后端服务。默认为 100\n        ring-buffer-size-in-half-open-state: 5 # 熔断器半开状态的缓冲区大小，会限制线程的并发量。例如，缓冲区为 10 则每次只会允许 10 个请求调用后端服务。默认为 10\n        wait-duration-in-open-state : 5000 # 熔断器从打开状态转变为半开状态等待的时间，单位：微秒\n        automatic-transition-from-open-to-half-open-enabled: true # 如果置为 true，当等待时间结束会自动由打开变为半开；若置为 false，则需要一个请求进入来触发熔断器状态转换。默认为 true\n        register-health-indicator: true # 是否注册到健康监测\n\n  # Resilience4j 的限流器配置项，对应 RateLimiterProperties 属性类\n  ratelimiter:\n    instances:\n      backendB:\n        limit-for-period: 1 # 每个周期内，允许的请求数。默认为 50\n        limit-refresh-period: 10s # 每个周期的时长，单位：微秒。默认为 500\n        timeout-duration: 5s # 被限流时，阻塞等待的时长，单位：微秒。默认为 5s\n        register-health-indicator: true # 是否注册到健康监测\n\n  # Resilience4j 的信号量 Bulkhead 配置项，对应 BulkheadConfigurationProperties 属性类\n  bulkhead:\n    instances:\n      backendC:\n        max-concurrent-calls: 1 # 并发调用数。默认为 25\n        max-wait-duration: 5s # 并发调用到达上限时，阻塞等待的时长，单位：微秒。默认为 0\n  # Resilience4j 的线程池 Bulkhead 配置项，对应 ThreadPoolBulkheadProperties 属性类\n  thread-pool-bulkhead:\n    instances:\n      backendD:\n        max-thread-pool-size: 1 # 线程池的最大大小。默认为 Runtime.getRuntime().availableProcessors()\n        core-thread-pool-size: 1 # 线程池的核心大小。默认为 Runtime.getRuntime().availableProcessors() - 1\n        queue-capacity: 200 # 线程池的队列大小。默认为 100\n        keep-alive-duration: 100s # 超过核心大小的线程，空闲存活时间。默认为 20 毫秒\n\n  # Resilience4j 的重试 Retry 配置项，对应 RetryProperties 属性类\n  retry:\n    instances:\n      backendE:\n        max-retry-Attempts: 3 # 最大重试次数。默认为 3\n        wait-duration: 5s # 下次重试的间隔，单位：微秒。默认为 500 毫秒\n        retry-exceptions: # 需要重试的异常列表。默认为空\n        ingore-exceptions: # 需要忽略的异常列表。默认为空\n\n  # Resilience4j 的超时限制器 TimeLimiter 配置项，对应 TimeLimiterProperties 属性类\n  timelimiter:\n    instances:\n      backendF:\n        timeout-duration: 1s # 等待超时时间，单位：微秒。默认为 1 秒\n        cancel-running-future: true # 当等待超时时，是否关闭取消线程。默认为 true\n"
  },
  {
    "path": "labx-24/labx-24-resilience4j-demo02/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>lab-59</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-24-resilience4j-demo02</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud CircuitBreaker Resilience4j 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>\n            <version>1.0.2.RELEASE</version>\n        </dependency>\n\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-24/labx-24-resilience4j-demo02/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx24.resilience4jdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.web.client.RestTemplate;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    @Bean\n    public RestTemplate restTemplate() {\n        return new RestTemplate();\n    }\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-24/labx-24-resilience4j-demo02/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/config/Resilience4jConfig.java",
    "content": "package cn.iocoder.springcloud.labx24.resilience4jdemo.config;\n\nimport io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;\nimport io.github.resilience4j.timelimiter.TimeLimiterConfig;\nimport org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JCircuitBreakerFactory;\nimport org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder;\nimport org.springframework.cloud.client.circuitbreaker.Customizer;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\nimport java.time.Duration;\nimport java.util.function.Consumer;\nimport java.util.function.Function;\n\n@Configuration\npublic class Resilience4jConfig {\n\n    @Bean\n    public Customizer<Resilience4JCircuitBreakerFactory> resilience4JCircuitBreakerFactoryCustomizer() {\n        return new Customizer<Resilience4JCircuitBreakerFactory>() {\n\n            @Override\n            public void customize(Resilience4JCircuitBreakerFactory resilience4JCircuitBreakerFactory) {\n                // 设置默认的配置\n                resilience4JCircuitBreakerFactory.configureDefault(new Function<String, Resilience4JConfigBuilder.Resilience4JCircuitBreakerConfiguration>() {\n\n                    @Override\n                    public Resilience4JConfigBuilder.Resilience4JCircuitBreakerConfiguration apply(String id) {\n                        // 创建 TimeLimiterConfig 对象\n                        TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.ofDefaults(); // 默认\n                        // 创建 CircuitBreakerConfig 对象\n                        CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.ofDefaults(); // 默认\n                        // 创建 Resilience4JCircuitBreakerConfiguration 对象\n                        return new Resilience4JConfigBuilder(id)\n                                .timeLimiterConfig(timeLimiterConfig)\n                                .circuitBreakerConfig(circuitBreakerConfig)\n                                .build();\n                    }\n\n                });\n                // 设置编号为 \"slow\" 的自定义配置\n                resilience4JCircuitBreakerFactory.configure(new Consumer<Resilience4JConfigBuilder>() {\n                    @Override\n                    public void accept(Resilience4JConfigBuilder resilience4JConfigBuilder) {\n                        // 创建 TimeLimiterConfig 对象\n                        TimeLimiterConfig timeLimiterConfig = TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(4)) // 自定义\n                                .build();\n                        // 创建 CircuitBreakerConfig 对象\n                        CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom() // 自定义\n                                .slidingWindow(5, 5, CircuitBreakerConfig.SlidingWindowType.COUNT_BASED)\n                                .build();\n                        // 设置 Resilience4JCircuitBreakerConfiguration 对象\n                        resilience4JConfigBuilder\n                                .timeLimiterConfig(timeLimiterConfig)\n                                .circuitBreakerConfig(circuitBreakerConfig);\n                    }\n                }, \"slow\");\n            }\n\n        };\n    }\n\n//    @Bean\n//    public Customizer<Resilience4JCircuitBreakerFactory> defaultCustomizer() {\n//        return factory -> factory.configureDefault(\n//                id -> new Resilience4JConfigBuilder(id)\n//                        .timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(4)).build())\n//                        .circuitBreakerConfig(CircuitBreakerConfig.ofDefaults())\n//                        .build());\n//    }\n\n//    @Bean\n//    public Customizer<Resilience4JCircuitBreakerFactory> slowCustomizer() {\n//        return factory -> factory.configure(builder -> builder\n//                .timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration.ofSeconds(2)).build())\n//                .circuitBreakerConfig(CircuitBreakerConfig.ofDefaults()),\n//                \"slow\");\n//    }\n\n}\n"
  },
  {
    "path": "labx-24/labx-24-resilience4j-demo02/src/main/java/cn/iocoder/springcloud/labx24/resilience4jdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx24.resilience4jdemo.controller;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.function.Function;\nimport java.util.function.Supplier;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private RestTemplate restTemplate;\n\n    @Autowired\n    private CircuitBreakerFactory circuitBreakerFactory;\n\n    @GetMapping(\"/get_user\")\n    public String getUser(@RequestParam(\"id\") Integer id) {\n        return circuitBreakerFactory.create(\"slow\").run(new Supplier<String>() {\n\n            @Override\n            public String get() {\n                logger.info(\"[getUser][准备调用 user-service 获取用户({})详情]\", id);\n                return restTemplate.getForEntity(\"http://127.0.0.1:18080/user/get?id=\"\n                        + id, String.class).getBody();\n            }\n\n        }, new Function<Throwable, String>() {\n\n            @Override\n            public String apply(Throwable throwable) {\n                logger.info(\"[getUserFallback][id({}) exception({})]\", id,\n                        throwable.getClass().getSimpleName());\n                return \"mock:User:\" + id;\n            }\n\n        });\n    }\n}\n"
  },
  {
    "path": "labx-24/labx-24-user-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-24</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-24-user-service</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-24/labx-24-user-service/src/main/java/cn/iocoder/springcloud/labx24/userservice/UserServiceApplication.java",
    "content": "package cn.iocoder.springcloud.labx24.userservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.List;\nimport java.util.stream.Collectors;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    @RestController\n    @RequestMapping(\"/user\")\n    public class UserController {\n\n        @GetMapping(\"/get\")\n        public String get(@RequestParam(\"id\") Integer id) {\n            return \"User:\" + id;\n        }\n\n        @GetMapping(\"/batch_get\")\n        public List<String> batchGet(@RequestParam(\"ids\") List<Integer> ids) {\n            return ids.stream().map(id -> \"User:\" + id).collect(Collectors.toList());\n        }\n\n    }\n\n    public static void main(String[] args) {\n        // 设置端口\n        System.setProperty(\"server.port\", \"18080\");\n\n        // 应用启动\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-24/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-24</artifactId>\n\n    <modules>\n        <module>labx-24-user-service</module>\n        <module>labx-24-resilience4j-demo01</module>\n        <module>labx-24-resilience4j-demo02</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-24/《芋道 Spring Cloud 服务容错 Resilience4j 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/Resilience4j/?github>\n"
  },
  {
    "path": "labx-25/labx-25-sc-zookeeper-discovery-demo01-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-25</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-25-sc-zookeeper-discovery-demo01-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Zookeeper Discovery 相关依赖，将 Zookeeper 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-25/labx-25-sc-zookeeper-discovery-demo01-consumer/src/main/java/cn/iocoder/springcloud/labx25/zookeeperdemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx25.zookeeperdemo.consumer;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.List;\n\n@SpringBootApplication\n// @EnableDiscoveryClient\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n    @Configuration\n    public class RestTemplateConfiguration {\n\n        @Bean\n        public RestTemplate restTemplate() {\n            return new RestTemplate();\n        }\n\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private DiscoveryClient discoveryClient;\n        @Autowired\n        private RestTemplate restTemplate;\n        @Autowired\n        private LoadBalancerClient loadBalancerClient;\n\n        @GetMapping(\"/hello\")\n        public String hello(String name) {\n            // 获得服务 `demo-provider` 的一个实例\n            ServiceInstance instance;\n            if (true) {\n                // 获取服务 `demo-provider` 对应的实例列表\n                List<ServiceInstance> instances = discoveryClient.getInstances(\"demo-provider\");\n                // 选择第一个\n                instance = instances.size() > 0 ? instances.get(0) : null;\n            } else {\n                instance = loadBalancerClient.choose(\"demo-provider\");\n            }\n            // 发起调用\n            if (instance == null) {\n                throw new IllegalStateException(\"获取不到实例\");\n            }\n            String targetUrl = instance.getUri() + \"/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-25/labx-25-sc-zookeeper-discovery-demo01-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n  cloud:\n    zookeeper:\n      connect-string: 127.0.0.1:2181\n      # Zookeeper 作为注册中心的配置项，对应 ZookeeperDiscoveryProperties 配置类\n      discovery:\n        root: /services # Zookeeper 数据存储的根节点，默认为 /services\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-25/labx-25-sc-zookeeper-discovery-demo01-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-25</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-25-sc-zookeeper-discovery-demo01-provider</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Zookeeper Discovery 相关依赖，将 Zookeeper 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-25/labx-25-sc-zookeeper-discovery-demo01-provider/src/main/java/cn/iocoder/springcloud/labx25/zookeeperdemo/provider/DemoProviderApplication.java",
    "content": "package cn.iocoder.springcloud.labx25.zookeeperdemo.provider;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class DemoProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoProviderApplication.class, args);\n    }\n\n    @RestController\n    static class TestController {\n\n        @GetMapping(\"/echo\")\n        public String echo(String name) {\n            return \"provider:\" + name;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-25/labx-25-sc-zookeeper-discovery-demo01-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider # Spring 应用名\n  cloud:\n    zookeeper:\n      connect-string: 127.0.0.1:2181\n      # Zookeeper 作为注册中心的配置项，对应 ZookeeperDiscoveryProperties 配置类\n      discovery:\n        root: /services # Zookeeper 数据存储的根节点，默认为 /services\n\nserver:\n  port: 18080 # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-25/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-25</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-25-sc-zookeeper-discovery-demo01-provider</module>\n        <module>labx-25-sc-zookeeper-discovery-demo01-consumer</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-25/《芋道 Spring Cloud 注册中心 ZooKeeper 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/ZooKeeper-Discovery/?github>\n"
  },
  {
    "path": "labx-26/labx-26-sc-zookeeper-config-auto-refresh/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-26</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-26-sc-zookeeper-config-auto-refresh</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Zookeeper Config 相关依赖，将 Zookeeper 作为配置中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zookeeper-config</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-26/labx-26-sc-zookeeper-config-auto-refresh/src/main/java/cn/iocoder/springcloud/labx26/zookeeperdemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx26.zookeeperdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-26/labx-26-sc-zookeeper-config-auto-refresh/src/main/java/cn/iocoder/springcloud/labx26/zookeeperdemo/config/OrderProperties.java",
    "content": "package cn.iocoder.springcloud.labx26.zookeeperdemo.config;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n//    /**\n//     * 配置描述\n//     */\n//    private String desc;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n//    public String getDesc() {\n//        return desc;\n//    }\n//\n//    public OrderProperties setDesc(String desc) {\n//        this.desc = desc;\n//        return this;\n//    }\n\n}\n"
  },
  {
    "path": "labx-26/labx-26-sc-zookeeper-config-auto-refresh/src/main/java/cn/iocoder/springcloud/labx26/zookeeperdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx26.zookeeperdemo.controller;\n\nimport cn.iocoder.springcloud.labx26.zookeeperdemo.config.OrderProperties;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/demo\")\n@RefreshScope\npublic class DemoController {\n\n    @Autowired\n    private OrderProperties orderProperties;\n\n    /**\n     * 测试 @ConfigurationProperties 注解的配置属性类\n     */\n    @GetMapping(\"/test01\")\n    public OrderProperties test01() {\n        return orderProperties;\n    }\n\n    @Value(value = \"${order.pay-timeout-seconds}\") // @NacosValue(value = \"${order.pay-timeout-seconds}\")\n    private Integer payTimeoutSeconds;\n    @Value(value = \"${order.create-frequency-seconds}\") // @NacosValue(value = \"${order.create-frequency-seconds}\")\n    private Integer createFrequencySeconds;\n\n    /**\n     * 测试 @Value 注解的属性\n     */\n    @GetMapping(\"/test02\")\n    public Map<String, Object> test02() {\n        Map<String, Object> results = new HashMap<String, Object>();\n        results.put(\"payTimeoutSeconds\", payTimeoutSeconds);\n        results.put(\"createFrequencySeconds\", createFrequencySeconds);\n        return results;\n    }\n\n}\n"
  },
  {
    "path": "labx-26/labx-26-sc-zookeeper-config-auto-refresh/src/main/java/cn/iocoder/springcloud/labx26/zookeeperdemo/listener/DemoEnvironmentChangeListener.java",
    "content": "package cn.iocoder.springcloud.labx26.zookeeperdemo.listener;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.context.environment.EnvironmentChangeEvent;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.core.env.ConfigurableEnvironment;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class DemoEnvironmentChangeListener implements ApplicationListener<EnvironmentChangeEvent> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private ConfigurableEnvironment environment;\n\n    @Override\n    public void onApplicationEvent(EnvironmentChangeEvent event) {\n        for (String key : event.getKeys()) {\n            logger.info(\"[onApplicationEvent][key({}) 最新 value 为 {}]\", key, environment.getProperty(key));\n        }\n    }\n\n}\n"
  },
  {
    "path": "labx-26/labx-26-sc-zookeeper-config-auto-refresh/src/main/resources/bootstrap.yaml",
    "content": "spring:\n  application:\n    name: demo-application\n\n  cloud:\n    zookeeper:\n      connect-string: 127.0.0.1:2181\n      # Zookeeper 作为配置中心的配置项，对应 ZookeeperConfigProperties 配置类\n      config:\n        root: /config # Zookeeper 数据存储的根节点，默认为 /config\n        default-context: application # 读取 Zookeeper 配置的默认目录，默认为 application\n        profile-separator: ',' # 多环境目录分隔符，默认为 ,\n        watcher:\n          enabled: true # 是否开启 Watch 功能，监听 Zookeeper 数据的变化。默认为 true，实现实时监听配置的更新\n"
  },
  {
    "path": "labx-26/labx-26-sc-zookeeper-config-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-26</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-26-sc-zookeeper-config-demo</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Zookeeper Config 相关依赖，将 Zookeeper 作为配置中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-zookeeper-config</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-26/labx-26-sc-zookeeper-config-demo/src/main/java/cn/iocoder/springcloud/labx26/zookeeperdemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx26.zookeeperdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-26/labx-26-sc-zookeeper-config-demo/src/main/java/cn/iocoder/springcloud/labx26/zookeeperdemo/config/OrderProperties.java",
    "content": "package cn.iocoder.springcloud.labx26.zookeeperdemo.config;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n//    /**\n//     * 配置描述\n//     */\n//    private String desc;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n//    public String getDesc() {\n//        return desc;\n//    }\n//\n//    public OrderProperties setDesc(String desc) {\n//        this.desc = desc;\n//        return this;\n//    }\n\n}\n"
  },
  {
    "path": "labx-26/labx-26-sc-zookeeper-config-demo/src/main/java/cn/iocoder/springcloud/labx26/zookeeperdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx26.zookeeperdemo.controller;\n\nimport cn.iocoder.springcloud.labx26.zookeeperdemo.config.OrderProperties;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private OrderProperties orderProperties;\n\n    /**\n     * 测试 @ConfigurationProperties 注解的配置属性类\n     */\n    @GetMapping(\"/test01\")\n    public OrderProperties test01() {\n        return orderProperties;\n    }\n\n    @Value(value = \"${order.pay-timeout-seconds}\") // @NacosValue(value = \"${order.pay-timeout-seconds}\")\n    private Integer payTimeoutSeconds;\n    @Value(value = \"${order.create-frequency-seconds}\") // @NacosValue(value = \"${order.create-frequency-seconds}\")\n    private Integer createFrequencySeconds;\n\n    /**\n     * 测试 @Value 注解的属性\n     */\n    @GetMapping(\"/test02\")\n    public Map<String, Object> test02() {\n        Map<String, Object> results = new HashMap<String, Object>();\n        results.put(\"payTimeoutSeconds\", payTimeoutSeconds);\n        results.put(\"createFrequencySeconds\", createFrequencySeconds);\n        return results;\n    }\n\n}\n"
  },
  {
    "path": "labx-26/labx-26-sc-zookeeper-config-demo/src/main/resources/bootstrap.yaml",
    "content": "spring:\n  application:\n    name: demo-application\n\n  cloud:\n    zookeeper:\n      connect-string: 127.0.0.1:2181\n      # Zookeeper 作为配置中心的配置项，对应 ZookeeperConfigProperties 配置类\n      config:\n        root: /config # Zookeeper 数据存储的根节点，默认为 /config\n        default-context: application # 读取 Zookeeper 配置的默认目录，默认为 application\n        profile-separator: ',' # 多环境目录分隔符，默认为 ,\n        watcher:\n          enabled: true # 是否开启 Watch 功能，监听 Zookeeper 数据的变化。默认为 true，实现实时监听配置的更新\n"
  },
  {
    "path": "labx-26/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-26</artifactId>\n\n    <modules>\n        <module>labx-26-sc-zookeeper-config-demo</module>\n        <module>labx-26-sc-zookeeper-config-auto-refresh</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-26/《芋道 Spring Cloud 配置中心 ZooKeeper 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/ZooKeeper-Config/?github>\n"
  },
  {
    "path": "labx-27/labx-27-sc-consul-discovery-demo01-consumer/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-27</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-27-sc-consul-discovery-demo01-consumer</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Consul Discovery 相关依赖，将 Consul 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-consul-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Boot Actuator 组件，因为需要通过它提供健康检查的接口给 Consul -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-27/labx-27-sc-consul-discovery-demo01-consumer/src/main/java/cn/iocoder/springcloud/labx27/consuldemo/consumer/DemoConsumerApplication.java",
    "content": "package cn.iocoder.springcloud.labx27.consuldemo.consumer;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.ServiceInstance;\nimport org.springframework.cloud.client.discovery.DiscoveryClient;\nimport org.springframework.cloud.client.loadbalancer.LoadBalancerClient;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.client.RestTemplate;\n\nimport java.util.List;\n\n@SpringBootApplication\n// @EnableDiscoveryClient\npublic class DemoConsumerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoConsumerApplication.class, args);\n    }\n\n    @Configuration\n    public class RestTemplateConfiguration {\n\n        @Bean\n        public RestTemplate restTemplate() {\n            return new RestTemplate();\n        }\n\n    }\n\n    @RestController\n    static class TestController {\n\n        @Autowired\n        private DiscoveryClient discoveryClient;\n        @Autowired\n        private RestTemplate restTemplate;\n        @Autowired\n        private LoadBalancerClient loadBalancerClient;\n\n        @GetMapping(\"/hello\")\n        public String hello(String name) {\n            // 获得服务 `demo-provider` 的一个实例\n            ServiceInstance instance;\n            if (true) {\n                // 获取服务 `demo-provider` 对应的实例列表\n                List<ServiceInstance> instances = discoveryClient.getInstances(\"demo-provider\");\n                // 选择第一个\n                instance = instances.size() > 0 ? instances.get(0) : null;\n            } else {\n                instance = loadBalancerClient.choose(\"demo-provider\");\n            }\n            // 发起调用\n            if (instance == null) {\n                throw new IllegalStateException(\"获取不到实例\");\n            }\n            String targetUrl = instance.getUri() + \"/echo?name=\" + name;\n            String response = restTemplate.getForObject(targetUrl, String.class);\n            // 返回结果\n            return \"consumer:\" + response;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-27/labx-27-sc-consul-discovery-demo01-consumer/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-consumer # Spring 应用名\n\n  cloud:\n    # Spring Cloud Consul 通用配置项，对应 ConsulProperties 类\n    consul:\n      host: 127.0.0.1 # Consul 主机\n      port: 8500 # Consul 端口\n      # Spring Cloud Consul Discovery 配置项，对应 ConsulDiscoveryProperties 类\n      discovery:\n        service-name: ${spring.application.name} # 注册到 Consul 的服务名，默认为 `spring.application.name` 配置项\n        health-check-path: /actuator/health # 健康检查的接口，默认为 /actuator/health，由 Spring Boot Actuator 提供\n\nserver:\n  port: 28080 # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-27/labx-27-sc-consul-discovery-demo01-provider/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-27</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-27-sc-consul-discovery-demo01-provider</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Consul Discovery 相关依赖，将 Consul 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-consul-discovery</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Boot Actuator 组件，因为需要通过它提供健康检查的接口给 Consul -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-27/labx-27-sc-consul-discovery-demo01-provider/src/main/java/cn/iocoder/springcloud/labx27/consuldemo/provider/DemoProviderApplication.java",
    "content": "package cn.iocoder.springcloud.labx27.consuldemo.provider;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.client.discovery.EnableDiscoveryClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@SpringBootApplication\n@EnableDiscoveryClient\npublic class DemoProviderApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoProviderApplication.class, args);\n    }\n\n    @RestController\n    static class TestController {\n\n        @GetMapping(\"/echo\")\n        public String echo(String name) {\n            return \"provider:\" + name;\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "labx-27/labx-27-sc-consul-discovery-demo01-provider/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: demo-provider # Spring 应用名\n  cloud:\n    # Spring Cloud Consul 通用配置项，对应 ConsulProperties 类\n    consul:\n      host: 127.0.0.1 # Consul 主机\n      port: 8500 # Consul 端口\n      # Spring Cloud Consul Discovery 配置项，对应 ConsulDiscoveryProperties 类\n      discovery:\n        service-name: ${spring.application.name} # 注册到 Consul 的服务名，默认为 `spring.application.name` 配置项\n        health-check-path: /actuator/health # 健康检查的接口，默认为 /actuator/health，由 Spring Boot Actuator 提供\n\nserver:\n  port: 18080 # 服务器端口。默认为 8080\n"
  },
  {
    "path": "labx-27/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-27</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-27-sc-consul-discovery-demo01-provider</module>\n        <module>labx-27-sc-consul-discovery-demo01-consumer</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-27/《芋道 Spring Cloud 注册中心 Consul 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/Consul-Discovery/?github>\n"
  },
  {
    "path": "labx-28/labx-28-sc-consul-config-auto-refresh/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-28</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-28-sc-consul-config-auto-refresh</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Consul Config 相关依赖，将 Consul 作为配置中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-consul-config</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-28/labx-28-sc-consul-config-auto-refresh/src/main/java/cn/iocoder/springcloud/labx28/consuldemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx28.consuldemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-28/labx-28-sc-consul-config-auto-refresh/src/main/java/cn/iocoder/springcloud/labx28/consuldemo/config/OrderProperties.java",
    "content": "package cn.iocoder.springcloud.labx28.consuldemo.config;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n//    /**\n//     * 配置描述\n//     */\n//    private String desc;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n//    public String getDesc() {\n//        return desc;\n//    }\n//\n//    public OrderProperties setDesc(String desc) {\n//        this.desc = desc;\n//        return this;\n//    }\n\n}\n"
  },
  {
    "path": "labx-28/labx-28-sc-consul-config-auto-refresh/src/main/java/cn/iocoder/springcloud/labx28/consuldemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx28.consuldemo.controller;\n\nimport cn.iocoder.springcloud.labx28.consuldemo.config.OrderProperties;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.cloud.context.config.annotation.RefreshScope;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/demo\")\n@RefreshScope\npublic class DemoController {\n\n    @Autowired\n    private OrderProperties orderProperties;\n\n    /**\n     * 测试 @ConfigurationProperties 注解的配置属性类\n     */\n    @GetMapping(\"/test01\")\n    public OrderProperties test01() {\n        return orderProperties;\n    }\n\n    @Value(value = \"${order.pay-timeout-seconds}\") // @NacosValue(value = \"${order.pay-timeout-seconds}\")\n    private Integer payTimeoutSeconds;\n    @Value(value = \"${order.create-frequency-seconds}\") // @NacosValue(value = \"${order.create-frequency-seconds}\")\n    private Integer createFrequencySeconds;\n\n    /**\n     * 测试 @Value 注解的属性\n     */\n    @GetMapping(\"/test02\")\n    public Map<String, Object> test02() {\n        Map<String, Object> results = new HashMap<String, Object>();\n        results.put(\"payTimeoutSeconds\", payTimeoutSeconds);\n        results.put(\"createFrequencySeconds\", createFrequencySeconds);\n        return results;\n    }\n\n}\n"
  },
  {
    "path": "labx-28/labx-28-sc-consul-config-auto-refresh/src/main/java/cn/iocoder/springcloud/labx28/consuldemo/listener/DemoEnvironmentChangeListener.java",
    "content": "package cn.iocoder.springcloud.labx28.consuldemo.listener;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.context.environment.EnvironmentChangeEvent;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.core.env.ConfigurableEnvironment;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class DemoEnvironmentChangeListener implements ApplicationListener<EnvironmentChangeEvent> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private ConfigurableEnvironment environment;\n\n    @Override\n    public void onApplicationEvent(EnvironmentChangeEvent event) {\n        for (String key : event.getKeys()) {\n            logger.info(\"[onApplicationEvent][key({}) 最新 value 为 {}]\", key, environment.getProperty(key));\n        }\n    }\n\n}\n"
  },
  {
    "path": "labx-28/labx-28-sc-consul-config-auto-refresh/src/main/resources/bootstrap.yaml",
    "content": "spring:\n  application:\n    name: demo-application\n\n  cloud:\n    # Spring Cloud Consul 通用配置项，对应 ConsulProperties 类\n    consul:\n      host: 127.0.0.1 # Consul 主机\n      port: 8500 # Consul 端口\n      # Spring Cloud Consul Config 配置项，对应 ConsulConfigProperties 类\n      config:\n        format: YAML # Consul 配置的格式\n        prefix: config # Consul 配置的目录\n        data-key: data # Consul 配置的文件\n        profile-separator: ',' # 多环境目录分隔符，默认为 ,\n"
  },
  {
    "path": "labx-28/labx-28-sc-consul-config-demo/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-28</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-28-sc-consul-config-demo</artifactId>\n\n    <properties>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Consul Config 相关依赖，将 Consul 作为配置中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-consul-config</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-28/labx-28-sc-consul-config-demo/src/main/java/cn/iocoder/springcloud/labx28/consuldemo/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx28.consuldemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(DemoApplication.class);\n    }\n\n}\n"
  },
  {
    "path": "labx-28/labx-28-sc-consul-config-demo/src/main/java/cn/iocoder/springcloud/labx28/consuldemo/config/OrderProperties.java",
    "content": "package cn.iocoder.springcloud.labx28.consuldemo.config;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n//    /**\n//     * 配置描述\n//     */\n//    private String desc;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n//    public String getDesc() {\n//        return desc;\n//    }\n//\n//    public OrderProperties setDesc(String desc) {\n//        this.desc = desc;\n//        return this;\n//    }\n\n}\n"
  },
  {
    "path": "labx-28/labx-28-sc-consul-config-demo/src/main/java/cn/iocoder/springcloud/labx28/consuldemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx28.consuldemo.controller;\n\nimport cn.iocoder.springcloud.labx28.consuldemo.config.OrderProperties;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @Autowired\n    private OrderProperties orderProperties;\n\n    /**\n     * 测试 @ConfigurationProperties 注解的配置属性类\n     */\n    @GetMapping(\"/test01\")\n    public OrderProperties test01() {\n        return orderProperties;\n    }\n\n    @Value(value = \"${order.pay-timeout-seconds}\") // @NacosValue(value = \"${order.pay-timeout-seconds}\")\n    private Integer payTimeoutSeconds;\n    @Value(value = \"${order.create-frequency-seconds}\") // @NacosValue(value = \"${order.create-frequency-seconds}\")\n    private Integer createFrequencySeconds;\n\n    /**\n     * 测试 @Value 注解的属性\n     */\n    @GetMapping(\"/test02\")\n    public Map<String, Object> test02() {\n        Map<String, Object> results = new HashMap<String, Object>();\n        results.put(\"payTimeoutSeconds\", payTimeoutSeconds);\n        results.put(\"createFrequencySeconds\", createFrequencySeconds);\n        return results;\n    }\n\n}\n"
  },
  {
    "path": "labx-28/labx-28-sc-consul-config-demo/src/main/resources/bootstrap.yaml",
    "content": "spring:\n  application:\n    name: demo-application\n\n  cloud:\n    # Spring Cloud Consul 通用配置项，对应 ConsulProperties 类\n    consul:\n      host: 127.0.0.1 # Consul 主机\n      port: 8500 # Consul 端口\n      # Spring Cloud Consul Config 配置项，对应 ConsulConfigProperties 类\n      config:\n        format: YAML # Consul 配置的格式\n        prefix: config # Consul 配置的目录\n        data-key: data # Consul 配置的文件\n        profile-separator: ',' # 多环境目录分隔符，默认为 ,\n"
  },
  {
    "path": "labx-28/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-28</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-28-sc-consul-config-demo</module>\n        <module>labx-28-sc-consul-config-auto-refresh</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-28/《芋道 Spring Cloud 配置中心 Consul 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/Consul-Config/?github>\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-29-spring-cloud-consul-bus</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-29-sc-bus-consul-demo-listener</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入基于 Consul 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-consul-bus</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener/src/main/java/cn/iocoder/springcloud/labx29/listenerdemo/ListenerDemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx29.listenerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.bus.jackson.RemoteApplicationEventScan;\n\n@SpringBootApplication\n@RemoteApplicationEventScan\npublic class ListenerDemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ListenerDemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener/src/main/java/cn/iocoder/springcloud/labx29/listenerdemo/event/UserRegisterEvent.java",
    "content": "package cn.iocoder.springcloud.labx29.listenerdemo.event;\n\nimport org.springframework.cloud.bus.event.RemoteApplicationEvent;\n\n/**\n * 用户注册事件\n */\npublic class UserRegisterEvent extends RemoteApplicationEvent {\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    public UserRegisterEvent() { // 序列化\n    }\n\n    public UserRegisterEvent(Object source, String originService, String destinationService, String username) {\n        super(source, originService);\n        this.username = username;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n}\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener/src/main/java/cn/iocoder/springcloud/labx29/listenerdemo/listener/UserRegisterListener.java",
    "content": "package cn.iocoder.springcloud.labx29.listenerdemo.listener;\n\nimport cn.iocoder.springcloud.labx29.listenerdemo.event.UserRegisterEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.stereotype.Component;\n\n/**\n * 用户注册事件的监听器\n */\n@Component\npublic class UserRegisterListener implements ApplicationListener<UserRegisterEvent> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void onApplicationEvent(UserRegisterEvent event) {\n        logger.info(\"[onApplicationEvent][监听到用户({}) 注册]\", event.getUsername());\n    }\n\n}\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: listener-demo\n\n  cloud:\n    # Consul\n    consul:\n      host: 127.0.0.1\n      port: 8500\n\nserver:\n  port: ${random.int[10000,19999]} # 随机端口，方便启动多个消费者\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener-actuator/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-29-spring-cloud-consul-bus</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-29-sc-bus-consul-demo-listener-actuator</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入基于 Consul 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-consul-bus</artifactId>\n        </dependency>\n\n        <!-- 实现对 Actuator 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-actuator</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener-actuator/src/main/java/cn/iocoder/springcloud/labx29/listenerdemo/ListenerDemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx29.listenerdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.bus.jackson.RemoteApplicationEventScan;\n\n@SpringBootApplication\n@RemoteApplicationEventScan\npublic class ListenerDemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ListenerDemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener-actuator/src/main/java/cn/iocoder/springcloud/labx29/listenerdemo/event/UserRegisterEvent.java",
    "content": "package cn.iocoder.springcloud.labx29.listenerdemo.event;\n\nimport org.springframework.cloud.bus.event.RemoteApplicationEvent;\n\n/**\n * 用户注册事件\n */\npublic class UserRegisterEvent extends RemoteApplicationEvent {\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    public UserRegisterEvent() { // 序列化\n    }\n\n    public UserRegisterEvent(Object source, String originService, String destinationService, String username) {\n        super(source, originService);\n        this.username = username;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n}\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener-actuator/src/main/java/cn/iocoder/springcloud/labx29/listenerdemo/listener/UserRegisterListener.java",
    "content": "package cn.iocoder.springcloud.labx29.listenerdemo.listener;\n\nimport cn.iocoder.springcloud.labx29.listenerdemo.event.UserRegisterEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.stereotype.Component;\n\n/**\n * 用户注册事件的监听器\n */\n@Component\npublic class UserRegisterListener implements ApplicationListener<UserRegisterEvent> {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void onApplicationEvent(UserRegisterEvent event) {\n        logger.info(\"[onApplicationEvent][监听到用户({}) 注册]\", event.getUsername());\n    }\n\n}\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener-actuator/src/main/java/org/springframework/cloud/consul/binder/ConsulSendingHandler.java",
    "content": "package org.springframework.cloud.consul.binder;\n\nimport com.ecwid.consul.v1.ConsulClient;\nimport com.ecwid.consul.v1.QueryParams;\nimport com.ecwid.consul.v1.Response;\nimport com.ecwid.consul.v1.event.model.Event;\nimport com.ecwid.consul.v1.event.model.EventParams;\nimport org.springframework.integration.handler.AbstractMessageHandler;\nimport org.springframework.messaging.Message;\n\n/**\n * Adapter that converts and sends Messages as Consul events.\n *\n * @author Spencer Gibb\n */\npublic class ConsulSendingHandler extends AbstractMessageHandler {\n\n    private final ConsulClient consul;\n\n    private final String eventName;\n\n    public ConsulSendingHandler(ConsulClient consul, String eventName) {\n        this.consul = consul;\n        this.eventName = eventName;\n    }\n\n    @Override\n    protected void handleMessageInternal(Message<?> message) {\n        if (this.logger.isTraceEnabled()) {\n            this.logger.trace(\"Publishing message\" + message);\n        }\n\n        // 转换成 String\n        Object payload = message.getPayload();\n        if (payload instanceof byte[]) {\n            payload = new String((byte[]) payload);\n        }\n\n        // TODO: support headers\n        // TODO: support consul event filters: NodeFilter, ServiceFilter, TagFilter\n        Response<Event> event = this.consul.eventFire(this.eventName, (String) payload,\n                new EventParams(), QueryParams.DEFAULT);\n        // TODO: return event?\n    }\n\n}\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-listener-actuator/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: listener-demo\n\n  cloud:\n    # Consul\n    consul:\n      host: 127.0.0.1\n      port: 8500\n\nserver:\n  port: 18080 # 随机端口，方便启动多个消费者\n\nmanagement:\n  endpoints:\n    # Actuator HTTP 配置项，对应 WebEndpointProperties 配置类\n    web:\n      exposure:\n        include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ，可以开放所有端点。\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-publisher/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-29-spring-cloud-consul-bus</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-29-sc-bus-consul-demo-publisher</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入基于 Consul 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-consul-bus</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-publisher/src/main/java/cn/iocoder/springcloud/labx29/publisherdemo/PublisherDemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx29.publisherdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\n//@RemoteApplicationEventScan\npublic class PublisherDemoApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(PublisherDemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-publisher/src/main/java/cn/iocoder/springcloud/labx29/publisherdemo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx29.publisherdemo.controller;\n\nimport cn.iocoder.springcloud.labx29.publisherdemo.event.UserRegisterEvent;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.cloud.bus.ServiceMatcher;\nimport org.springframework.context.ApplicationEventPublisher;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Autowired\n    private ApplicationEventPublisher applicationEventPublisher;\n\n    @Autowired\n    private ServiceMatcher busServiceMatcher;\n\n    @GetMapping(\"/register\")\n    public String register(String username) {\n        // ... 执行注册逻辑\n        logger.info(\"[register][执行用户({}) 的注册逻辑]\", username);\n\n        // ... 发布\n        applicationEventPublisher.publishEvent(new UserRegisterEvent(this, busServiceMatcher.getServiceId(),\n                null, username));\n        return \"success\";\n    }\n\n}\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-publisher/src/main/java/cn/iocoder/springcloud/labx29/publisherdemo/event/UserRegisterEvent.java",
    "content": "package cn.iocoder.springcloud.labx29.publisherdemo.event;\n\nimport org.springframework.cloud.bus.event.RemoteApplicationEvent;\n\n/**\n * 用户注册事件\n */\npublic class UserRegisterEvent extends RemoteApplicationEvent {\n\n    /**\n     * 用户名\n     */\n    private String username;\n\n    public UserRegisterEvent() { // 序列化\n    }\n\n    public UserRegisterEvent(Object source, String originService, String destinationService, String username) {\n        super(source, originService);\n        this.username = username;\n    }\n\n    public String getUsername() {\n        return username;\n    }\n\n}\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-publisher/src/main/java/org/springframework/cloud/consul/binder/ConsulSendingHandler.java",
    "content": "package org.springframework.cloud.consul.binder;\n\nimport com.ecwid.consul.v1.ConsulClient;\nimport com.ecwid.consul.v1.QueryParams;\nimport com.ecwid.consul.v1.Response;\nimport com.ecwid.consul.v1.event.model.Event;\nimport com.ecwid.consul.v1.event.model.EventParams;\nimport org.springframework.integration.handler.AbstractMessageHandler;\nimport org.springframework.messaging.Message;\n\n/**\n * Adapter that converts and sends Messages as Consul events.\n *\n * @author Spencer Gibb\n */\npublic class ConsulSendingHandler extends AbstractMessageHandler {\n\n    private final ConsulClient consul;\n\n    private final String eventName;\n\n    public ConsulSendingHandler(ConsulClient consul, String eventName) {\n        this.consul = consul;\n        this.eventName = eventName;\n    }\n\n    @Override\n    protected void handleMessageInternal(Message<?> message) {\n        if (this.logger.isTraceEnabled()) {\n            this.logger.trace(\"Publishing message\" + message);\n        }\n\n        // 转换成 String\n        Object payload = message.getPayload();\n        if (payload instanceof byte[]) {\n            payload = new String((byte[]) payload);\n        }\n\n        // TODO: support headers\n        // TODO: support consul event filters: NodeFilter, ServiceFilter, TagFilter\n        Response<Event> event = this.consul.eventFire(this.eventName, (String) payload,\n                new EventParams(), QueryParams.DEFAULT);\n        // TODO: return event?\n    }\n\n}\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-bus-consul-demo-publisher/src/main/resources/application.yml",
    "content": "server:\n  port: 8081\n\nspring:\n  application:\n    name: publisher-demo\n\n  cloud:\n    # Consul\n    consul:\n      host: 127.0.0.1\n      port: 8500\n    # Bus 相关配置项，对应 BusProperties\n    bus:\n      enabled: true # 是否开启，默认为 true\n      destination: springCloudBus # 目标消息队列，默认为 springCloudBus\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-config-server-git-auto-refresh-by-bus/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-29-spring-cloud-consul-bus</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-29-sc-config-server-git-auto-refresh-by-bus</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Stream Kafka 相关依赖，将 Kafka 作为消息队列，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-config-server</artifactId>\n        </dependency>\n\n        <!-- 引入基于 Consul 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-consul-bus</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Config Monitor 依赖，提供 `/monitor` 接口，用于刷新配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-config-monitor</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-config-server-git-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx29/configserverdemo/ConfigServerApplication.java",
    "content": "package cn.iocoder.springcloud.labx29.configserverdemo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.cloud.config.server.EnableConfigServer;\n\n@SpringBootApplication\n@EnableConfigServer\npublic class ConfigServerApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ConfigServerApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-config-server-git-auto-refresh-by-bus/src/main/resources/application.yml",
    "content": "server:\n  port: 8888\n\nspring:\n  application:\n    name: demo-config-server\n\n  profiles:\n    active: git # 使用的 Spring Cloud Config Server 的存储器方案\n  # Spring Cloud Config 相关配置项\n  cloud:\n    config:\n      server:\n        # Spring Cloud Config Server 的 Git 存储器的配置项，对应 MultipleJGitEnvironmentProperties 类\n        git:\n          uri: https://github.com/YunaiV/demo-config-server.git # Git 仓库地址\n          search-paths: / # 读取文件的根地址\n          default-label: master # 使用的默认分支，默认为 master\n  #          username: ${CODING_USERNAME} # 账号\n  #          password: ${CODING_PASSWORD} # 密码\n\n    # Consul\n    consul:\n      host: 127.0.0.1\n      port: 8500\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-config-user-application-auto-refresh-by-bus/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-29-spring-cloud-consul-bus</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-29-sc-config-user-application-auto-refresh-by-bus</artifactId>\n\n    <properties>\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 SpringMVC 相关依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Config Client 相关依赖，作为配置中心的客户端，并实现自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-config</artifactId>\n        </dependency>\n\n        <!-- 引入基于 Consul 的 Spring Cloud Bus 的实现的依赖，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>org.springframework.cloud</groupId>\n            <artifactId>spring-cloud-starter-consul-bus</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-config-user-application-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx29/userapplication/UserApplication.java",
    "content": "package cn.iocoder.springcloud.labx29.userapplication;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(UserApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-config-user-application-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx29/userapplication/config/OrderProperties.java",
    "content": "package cn.iocoder.springcloud.labx29.userapplication.config;\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ConfigurationProperties(prefix = \"order\")\npublic class OrderProperties {\n\n    /**\n     * 订单支付超时时长，单位：秒。\n     */\n    private Integer payTimeoutSeconds;\n\n    /**\n     * 订单创建频率，单位：秒\n     */\n    private Integer createFrequencySeconds;\n\n    public Integer getPayTimeoutSeconds() {\n        return payTimeoutSeconds;\n    }\n\n    public OrderProperties setPayTimeoutSeconds(Integer payTimeoutSeconds) {\n        this.payTimeoutSeconds = payTimeoutSeconds;\n        return this;\n    }\n\n    public Integer getCreateFrequencySeconds() {\n        return createFrequencySeconds;\n    }\n\n    public OrderProperties setCreateFrequencySeconds(Integer createFrequencySeconds) {\n        this.createFrequencySeconds = createFrequencySeconds;\n        return this;\n    }\n\n}\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-config-user-application-auto-refresh-by-bus/src/main/java/cn/iocoder/springcloud/labx29/userapplication/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx29.userapplication.controller;\n\nimport cn.iocoder.springcloud.labx29.userapplication.config.OrderProperties;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.cloud.context.config.annotation.RefreshScope;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RestController;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n@RestController\n@RequestMapping(\"/demo\")\n@RefreshScope\npublic class DemoController {\n\n    @Autowired\n    private OrderProperties orderProperties;\n\n    /**\n     * 测试 @ConfigurationProperties 注解的配置属性类\n     */\n    @GetMapping(\"/test01\")\n    public OrderProperties test01() {\n        return orderProperties;\n    }\n\n    @Value(value = \"${order.pay-timeout-seconds}\")\n    private Integer payTimeoutSeconds;\n    @Value(value = \"${order.create-frequency-seconds}\")\n    private Integer createFrequencySeconds;\n\n    /**\n     * 测试 @Value 注解的属性\n     */\n    @GetMapping(\"/test02\")\n    public Map<String, Object> test02() {\n        Map<String, Object> result = new HashMap<>();\n        result.put(\"payTimeoutSeconds\", payTimeoutSeconds);\n        result.put(\"createFrequencySeconds\", createFrequencySeconds);\n        return result;\n    }\n\n}\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-config-user-application-auto-refresh-by-bus/src/main/resources/application.yml",
    "content": "spring:\n  cloud:\n    # Consul\n    consul:\n      host: 127.0.0.1\n      port: 8500\n\nserver:\n  port: 8081\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/labx-29-sc-config-user-application-auto-refresh-by-bus/src/main/resources/bootstrap.yml",
    "content": "spring:\n  application:\n    name: user-application\n\n  cloud:\n    # Spring Cloud Config Client 配置项，对应 ConfigClientProperties 类\n    config:\n      uri: http://127.0.0.1:8888 # Spring Cloud Config Server 的地址\n      name: ${spring.application.name} # 读取的配置文件的名字，默认为 ${spring.application.name}\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-29-spring-cloud-consul-bus</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-29-sc-bus-consul-demo-publisher</module>\n        <module>labx-29-sc-bus-consul-demo-listener</module>\n        <module>labx-29-sc-bus-consul-demo-listener-actuator</module>\n\n        <module>labx-29-sc-config-server-git-auto-refresh-by-bus</module>\n        <module>labx-29-sc-config-user-application-auto-refresh-by-bus</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-29-spring-cloud-consul-bus/《芋道 Spring Cloud 消息总线 Bus Consul 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/Bus-Consul/?github>\n"
  },
  {
    "path": "labx-30-spring-cloud-grpc/labx-30-grpc-cloud/labx-30-grpc-cloud-application/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-30-grpc-cloud</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-30-grpc-cloud-application</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 API 项目 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-30-grpc-cloud-user-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 gRPC Client Starter 依赖，实现对 gRPC 的自动配置 -->\n        <dependency>\n            <groupId>net.devh</groupId>\n            <artifactId>grpc-client-spring-boot-starter</artifactId>\n            <version>2.8.0.RELEASE</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-30-spring-cloud-grpc/labx-30-grpc-cloud/labx-30-grpc-cloud-application/src/main/java/cn/iocoder/springcloud/labx30/demo/DemoApplication.java",
    "content": "package cn.iocoder.springcloud.labx30.demo;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class DemoApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(DemoApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-30-spring-cloud-grpc/labx-30-grpc-cloud/labx-30-grpc-cloud-application/src/main/java/cn/iocoder/springcloud/labx30/demo/controller/DemoController.java",
    "content": "package cn.iocoder.springcloud.labx30.demo.controller;\n\nimport cn.iocoder.springcloud.labx30.userservice.api.*;\nimport net.devh.boot.grpc.client.inject.GrpcClient;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RequestMapping;\nimport org.springframework.web.bind.annotation.RequestParam;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\n@RequestMapping(\"/demo\")\npublic class DemoController {\n\n    @GrpcClient(\"user-service\")\n    private UserServiceGrpc.UserServiceBlockingStub userServiceGrpc;\n\n    @GetMapping(\"/get\")\n    public String get(@RequestParam(\"id\") Integer id) {\n        // 创建请求\n        UserGetRequest request = UserGetRequest.newBuilder().setId(id).build();\n        // 执行 gRPC 请求\n        UserGetResponse response = userServiceGrpc.get(request);\n        // 响应\n        return response.getName();\n    }\n\n    @GetMapping(\"/create\") // 为了方便测试，实际使用 @PostMapping\n    public Integer create(@RequestParam(\"name\") String name,\n                          @RequestParam(\"gender\") Integer gender) {\n        // 创建请求\n        UserCreateRequest request = UserCreateRequest.newBuilder()\n                .setName(name).setGender(gender).build();\n        // 执行 gRPC 请求\n        UserCreateResponse response = userServiceGrpc.create(request);\n        // 响应\n        return response.getId();\n    }\n\n}\n"
  },
  {
    "path": "labx-30-spring-cloud-grpc/labx-30-grpc-cloud/labx-30-grpc-cloud-application/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: demo-application # 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\ngrpc:\n  # gRPC 客户端配置，对应 GrpcChannelsProperties 配置类的映射\n  client:\n    user-service:\n      negotiation-type: plaintext\n"
  },
  {
    "path": "labx-30-spring-cloud-grpc/labx-30-grpc-cloud/labx-30-grpc-cloud-user-service/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-30-grpc-cloud</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-30-grpc-cloud-user-service</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <spring.boot.version>2.2.4.RELEASE</spring.boot.version>\n        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>\n        <spring.cloud.alibaba.version>2.2.0.RELEASE</spring.cloud.alibaba.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n    </properties>\n\n    <!--\n        引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件，进行依赖版本的管理，防止不兼容。\n        在 https://dwz.cn/mcLIfNKt 文章中，Spring Cloud Alibaba 开发团队推荐了三者的依赖关系\n     -->\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-parent</artifactId>\n                <version>${spring.boot.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.springframework.cloud</groupId>\n                <artifactId>spring-cloud-dependencies</artifactId>\n                <version>${spring.cloud.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n            <dependency>\n                <groupId>com.alibaba.cloud</groupId>\n                <artifactId>spring-cloud-alibaba-dependencies</artifactId>\n                <version>${spring.cloud.alibaba.version}</version>\n                <type>pom</type>\n                <scope>import</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- 引入 API 项目 -->\n        <dependency>\n            <groupId>cn.iocoder.springboot.labs</groupId>\n            <artifactId>labx-30-grpc-cloud-user-service-api</artifactId>\n            <version>1.0-SNAPSHOT</version>\n        </dependency>\n\n        <!-- 实现对 SpringMVC 的自动化配置 -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-web</artifactId>\n        </dependency>\n\n        <!-- 引入 gRPC Server Starter 依赖，实现对 gRPC 的自动配置 -->\n        <dependency>\n            <groupId>net.devh</groupId>\n            <artifactId>grpc-server-spring-boot-starter</artifactId>\n            <version>2.8.0.RELEASE</version>\n        </dependency>\n\n        <!-- 引入 Spring Cloud Alibaba Nacos Discovery 相关依赖，将 Nacos 作为注册中心，并实现对其的自动配置 -->\n        <dependency>\n            <groupId>com.alibaba.cloud</groupId>\n            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>\n        </dependency>\n    </dependencies>\n\n</project>\n"
  },
  {
    "path": "labx-30-spring-cloud-grpc/labx-30-grpc-cloud/labx-30-grpc-cloud-user-service/src/main/java/cn/iocoder/springcloud/labx30/userservice/UserServiceApplication.java",
    "content": "package cn.iocoder.springcloud.labx30.userservice;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class UserServiceApplication {\n\n    public static void main(String[] args) {\n        // 启动 Spring Boot 应用\n        SpringApplication.run(UserServiceApplication.class, args);\n    }\n\n}\n"
  },
  {
    "path": "labx-30-spring-cloud-grpc/labx-30-grpc-cloud/labx-30-grpc-cloud-user-service/src/main/java/cn/iocoder/springcloud/labx30/userservice/rpc/UserServiceGrpcImpl.java",
    "content": "package cn.iocoder.springcloud.labx30.userservice.rpc;\n\nimport cn.iocoder.springcloud.labx30.userservice.api.*;\nimport io.grpc.stub.StreamObserver;\nimport net.devh.boot.grpc.server.service.GrpcService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n@GrpcService\npublic class UserServiceGrpcImpl extends UserServiceGrpc.UserServiceImplBase {\n\n    private Logger logger = LoggerFactory.getLogger(getClass());\n\n    @Override\n    public void get(UserGetRequest request, StreamObserver<UserGetResponse> responseObserver) {\n        logger.info(\"[get]\");\n        // 创建响应对象\n        UserGetResponse.Builder builder = UserGetResponse.newBuilder();\n        builder.setId(request.getId())\n                .setName(\"没有昵称：\" + request.getId())\n                .setGender(request.getId() % 2 + 1);\n        // 返回响应\n        responseObserver.onNext(builder.build());\n        responseObserver.onCompleted();\n    }\n\n    @Override\n    public void create(UserCreateRequest request, StreamObserver<UserCreateResponse> responseObserver) {\n        logger.info(\"[create]\");\n        // 创建响应对象\n        UserCreateResponse.Builder builder = UserCreateResponse.newBuilder();\n        builder.setId((int) (System.currentTimeMillis() / 1000));\n        // 返回响应\n        responseObserver.onNext(builder.build());\n        responseObserver.onCompleted();\n    }\n\n}\n"
  },
  {
    "path": "labx-30-spring-cloud-grpc/labx-30-grpc-cloud/labx-30-grpc-cloud-user-service/src/main/resources/application.yml",
    "content": "spring:\n  application:\n    name: user-service # 应用名\n  cloud:\n    nacos:\n      # Nacos 作为注册中心的配置项，对应 NacosDiscoveryProperties 配置类\n      discovery:\n        server-addr: 127.0.0.1:8848 # Nacos 服务器地址\n\ngrpc:\n  # gRPC 服务器配置，对应 GrpcServerProperties 配置类\n  server:\n    port: 0 # gRPC Server 随机端口\n\nserver:\n  port: 0 # Web Server 随机端口\n"
  },
  {
    "path": "labx-30-spring-cloud-grpc/labx-30-grpc-cloud/labx-30-grpc-cloud-user-service-api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-30-grpc-cloud</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-30-grpc-cloud-user-service-api</artifactId>\n\n    <properties>\n        <!-- 依赖相关配置 -->\n        <io.grpc.version>1.30.0</io.grpc.version>\n        <!-- 插件相关配置 -->\n        <maven.compiler.target>1.8</maven.compiler.target>\n        <maven.compiler.source>1.8</maven.compiler.source>\n        <os-maven-plugin.version>1.6.2</os-maven-plugin.version>\n        <protobuf-maven-plugin.version>0.6.1</protobuf-maven-plugin.version>\n    </properties>\n\n    <dependencies>\n        <!-- 引入 gRPC Protobuf 依赖，因为使用它作为序列化库 -->\n        <dependency>\n            <groupId>io.grpc</groupId>\n            <artifactId>grpc-protobuf</artifactId>\n            <version>${io.grpc.version}</version>\n        </dependency>\n        <!-- 引入 gRPC Stub 依赖，因为使用它作为 gRPC 客户端库 -->\n        <dependency>\n            <groupId>io.grpc</groupId>\n            <artifactId>grpc-stub</artifactId>\n            <version>${io.grpc.version}</version>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <extensions>\n            <!-- os-maven-plugin 插件，从 OS 系统中获取参数 -->\n            <extension>\n                <groupId>kr.motd.maven</groupId>\n                <artifactId>os-maven-plugin</artifactId>\n                <version>${os-maven-plugin.version}</version>\n            </extension>\n        </extensions>\n        <plugins>\n            <!-- protobuf-maven-plugin 插件，通过 protobuf 文件，生成 Service 和 Message 类 -->\n            <plugin>\n                <groupId>org.xolstice.maven.plugins</groupId>\n                <artifactId>protobuf-maven-plugin</artifactId>\n                <version>${protobuf-maven-plugin.version}</version>\n                <configuration>\n                    <pluginId>grpc-java</pluginId>\n                    <protocArtifact>com.google.protobuf:protoc:3.9.1:exe:${os.detected.classifier}</protocArtifact>\n                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:${io.grpc.version}:exe:${os.detected.classifier}</pluginArtifact>\n                </configuration>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>compile</goal>\n                            <goal>compile-custom</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>\n"
  },
  {
    "path": "labx-30-spring-cloud-grpc/labx-30-grpc-cloud/labx-30-grpc-cloud-user-service-api/src/main/proto/UserService.proto",
    "content": "syntax = \"proto3\";\noption java_multiple_files = true;\n\npackage cn.iocoder.springcloud.labx30.userservice.api;\n\nmessage UserGetRequest {\n    int32 id = 1;\n}\n\nmessage UserGetResponse {\n    int32 id = 1;\n    string name = 2;\n    int32 gender = 3;\n}\n\nmessage UserCreateRequest {\n    string name = 1;\n    int32 gender = 2;\n}\n\nmessage UserCreateResponse {\n    int32 id = 1;\n}\n\nservice UserService {\n\n    rpc get(UserGetRequest) returns (UserGetResponse);\n\n    rpc create(UserCreateRequest) returns (UserCreateResponse);\n\n}\n"
  },
  {
    "path": "labx-30-spring-cloud-grpc/labx-30-grpc-cloud/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labx-30-spring-cloud-grpc</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-30-grpc-cloud</artifactId>\n    <packaging>pom</packaging>\n    <modules>\n        <module>labx-30-grpc-cloud-user-service</module>\n        <module>labx-30-grpc-cloud-user-service-api</module>\n        <module>labx-30-grpc-cloud-application</module>\n    </modules>\n\n\n</project>\n"
  },
  {
    "path": "labx-30-spring-cloud-grpc/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <parent>\n        <artifactId>labs-parent</artifactId>\n        <groupId>cn.iocoder.springboot.labs</groupId>\n        <version>1.0-SNAPSHOT</version>\n    </parent>\n    <modelVersion>4.0.0</modelVersion>\n\n    <artifactId>labx-30-spring-cloud-grpc</artifactId>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>labx-30-grpc-cloud</module>\n    </modules>\n\n</project>\n"
  },
  {
    "path": "labx-30-spring-cloud-grpc/《芋道 Spring Cloud gRPC 入门》.md",
    "content": "<http://www.iocoder.cn/Spring-Cloud/gRPC/?github>\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>cn.iocoder.springboot.labs</groupId>\n    <artifactId>labs-parent</artifactId>\n    <packaging>pom</packaging>\n    <version>1.0-SNAPSHOT</version>\n    <modules>\n\n        <!-- Spring Boot 示例 -->\n<!--        <module>lab-01-spring-security</module>-->\n<!--        <module>lab-02-spring-security-oauth</module>-->\n<!--        <module>lab-03-kafka</module>-->\n<!--        <module>lab-04-rabbitmq</module>-->\n<!--        <module>lab-05-benchmark-tomcat-jetty-undertow</module>-->\n<!--        <module>lab-06</module>-->\n<!--        <module>lab-07</module>-->\n<!--        <module>lab-11-spring-data-redis</module>-->\n<!--        <module>lab-12-mybatis</module>-->\n<!--        <module>lab-13-spring-data-jpa</module>-->\n<!--        <module>lab-14-spring-jdbc-template</module>-->\n<!--        <module>lab-15-spring-data-es</module>-->\n<!--        <module>lab-16-spring-data-mongo</module>-->\n<!--        <module>lab-17</module>-->\n<!--        <module>lab-18</module>-->\n<!--        <module>lab-19</module>-->\n<!--        <module>lab-20</module>-->\n<!--        <module>lab-21</module>-->\n<!--        <module>lab-22</module>-->\n<!--        <module>lab-23</module>-->\n<!--        <module>lab-24</module>-->\n<!--        <module>lab-25</module>-->\n<!--        <module>lab-26</module>-->\n<!--        <module>lab-27</module>-->\n<!--        <module>lab-28</module>-->\n<!--        <module>lab-29</module>-->\n<!--        <module>lab-30</module>-->\n<!--        <module>lab-31</module>-->\n<!--        <module>lab-32</module>-->\n<!--        <module>lab-33</module>-->\n<!--        <module>lab-34</module>-->\n<!--        <module>lab-35</module>-->\n<!--        <module>lab-36</module>-->\n<!--        <module>lab-37</module>-->\n<!--        <module>lab-38</module>-->\n<!--        <module>lab-39</module>-->\n<!--        <module>lab-40</module>-->\n<!--        <module>lab-41</module>-->\n<!--        <module>lab-42</module>-->\n<!--        <module>lab-43</module>-->\n<!--        <module>lab-44</module>-->\n<!--        <module>lab-45</module>-->\n<!--        <module>lab-46</module>-->\n<!--        <module>lab-47</module>-->\n<!--        <module>lab-48-hot-swap</module>-->\n<!--        <module>lab-49</module>-->\n<!--        <module>lab-50</module>-->\n<!--        <module>lab-51</module>-->\n<!--        <module>lab-52</module>-->\n<!--        <module>lab-53</module>-->\n<!--        <module>lab-54</module>-->\n<!--        <module>lab-55</module>-->\n<!--        <module>lab-56</module>-->\n<!--        <module>lab-57</module>-->\n<!--        <module>lab-58</module>-->\n<!--        <module>lab-59</module>-->\n<!--        <module>lab-60</module>-->\n<!--        <module>lab-61</module>-->\n<!--        <module>lab-62</module>-->\n<!--        <module>lab-63</module>-->\n<!--        <module>lab-64</module>-->\n<!--        <module>lab-65</module>-->\n<!--        <module>lab-66</module>-->\n<!--        <module>lab-67</module>-->\n<!--        <module>lab-68-spring-security-oauth</module>-->\n<!--        <module>lab-69-proxy</module>-->\n<!--        <module>lab-70-db-doc</module>-->\n<!--        <module>lab-71-http-debug</module>-->\n\n        <!-- Spring Cloud 示例 -->\n<!--        <module>labx-01-spring-cloud-alibaba-nacos-discovery</module>-->\n<!--        <module>labx-02-spring-cloud-netflix-ribbon</module>-->\n<!--        <module>labx-03-spring-cloud-feign</module>-->\n<!--        <module>labx-04-spring-cloud-alibaba-sentinel</module>-->\n<!--        <module>labx-05-spring-cloud-alibaba-nacos-config</module>-->\n<!--        <module>labx-06-spring-cloud-stream-rocketmq</module>-->\n<!--        <module>labx-07-spring-cloud-alibaba-dubbo</module>-->\n<!--        <module>labx-08-spring-cloud-gateway</module>-->\n<!--        <module>labx-09-spring-cloud-apollo</module>-->\n<!--        <module>labx-10-spring-cloud-stream-rabbitmq</module>-->\n<!--        <module>labx-11-spring-cloud-stream-kafka</module>-->\n<!--        <module>labx-12-spring-cloud-config</module>-->\n<!--        <module>labx-13</module>-->\n<!--        <module>labx-14</module>-->\n<!--        <module>labx-15</module>-->\n<!--        <module>labx-16</module>-->\n<!--        <module>labx-17</module>-->\n<!--        <module>labx-18</module>-->\n<!--        <module>labx-19</module>-->\n<!--        <module>labx-20</module>-->\n<!--        <module>labx-21</module>-->\n<!--        <module>labx-22</module>-->\n<!--        <module>labx-23</module>-->\n<!--        <module>labx-24</module>-->\n<!--        <module>labx-25</module>-->\n<!--        <module>labx-26</module>-->\n<!--        <module>labx-27</module>-->\n<!--        <module>labx-28</module>-->\n<!--        <module>labx-28</module>-->\n<!--        <module>labx-29-spring-cloud-consul-bus</module>-->\n<!--        <module>labx-30-spring-cloud-grpc</module>-->\n        <module>lab-72-minio</module>\n    </modules>\n\n</project>\n"
  }
]